Руководство по Hibernate. Виды связей. SortedMap Mapping.

Коллекция Java, в которой хранятся только уникальные (разрешенно хранение только уникальных элементов) пары “ключ – значение” и в которой все элементы упорядочены, называется SortedMap.

SortedMap связывается (mapped) с помощью элемента <map> и инициализируется с помощью java.util.TreeMap.

Для понимания того, как это работает на практике, рассмотрим пример простого приложения.

Пример:

Исходный код проекта можно скачать по ЭТОЙ ССЫЛКЕ.

Шаг 1. Создадим две таблицы в бахе данных (далее – БД)
HIBERNATE_DEVELOPERS


CREATE TABLE HIBERNATE_DEVELOPERS(
   ID INT NOT NULL AUTO_INCREMENT,
   FIRST_NAME VARCHAR(50) DEFAULT NULL,
   LAST_NAME VARCHAR(50) DEFAULT NULL,
   SPECIALTY VARCHAR(50) DEFAULT NULL,
   EXPERIENCE INT DEFAULT NULL,
   PRIMARY KEY(ID)
);

HIBERNATE_PROJECTS

create table HIBERNATE_PROJECTS (
   id INT NOT NULL auto_increment,
   SPHERE VARCHAR(50) default NULL,
   PROJECT_NAME VARCHAR(50) default NULL,
   COMPANY VARCHAR(50) default NULL,
   DEVELOPER_ID INT default NULL,
   PRIMARY KEY (id)
);

Мы будем использовать отношение one-to-many между таблицами HIBERNATE_DEVELOPERS и HIBERNATE_PROJECTS.

Шаг 2. Создадим POJO – классы

Developer.java


package net.proselyte.hibernate.mappings.sortedmap;

import java.util.SortedMap;

public class Developer {
    private int id;
    private String firstName;
    private String lastName;
    private String specialty;
    private int experience;
    private SortedMap<String, Project> projects;

    /**
     * Default Constructor
     */
    public Developer() {
    }

    /**
     * Plain constructor
     */
    public Developer(String firstName, String lastName, String specialty, int experience) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.specialty = specialty;
        this.experience = experience;
    }


    /**
     * Getters and Setters
     */
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getSpecialty() {
        return specialty;
    }

    public void setSpecialty(String specialty) {
        this.specialty = specialty;
    }

    public int getExperience() {
        return experience;
    }

    public void setExperience(int experience) {
        this.experience = experience;
    }

    public SortedMap<String, Project> getProjects() {
        return projects;
    }

    public void setProjects(SortedMap<String, Project> projects) {
        this.projects = projects;
    }

    /**
     * toString method (optional)
     */
    @Override
    public String toString() {
        return "Developer:\n" +
                "id: " + id +
                "\nFirst Name: " + firstName + "\n" +
                "Last Name: " + lastName + "\n" +
                "Specialty: " + specialty + "\n" +
                "Experience: " + experience + "\n";
    }
}


Project.java


package net.proselyte.hibernate.mappings.sortedmap;

public class Project implements Comparable{
    private int id;
    private String projectName;
    private String companyName;

    /**
     * Constructors
     */
    public Project() {
    }

    public Project(String projectName, String companyName) {
        this.projectName = projectName;
        this.companyName = companyName;
    }


    /**
     * Getters and Setters
     */
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getProjectName() {
        return projectName;
    }

    public void setProjectName(String projectName) {
        this.projectName = projectName;
    }

    public String getCompanyName() {
        return companyName;
    }


    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public int compareTo(String that){
        final int BEFORE = -1;
        final int AFTER = 1;

        if (that == null) {
            return BEFORE;
        }

        Comparable thisProject = this;
        Comparable thatProject = that;

        if(thisProject == null) {
            return AFTER;
        } else if(thatProject == null) {
            return BEFORE;
        } else {
            return thisProject.compareTo(thatProject);
        }
    }

    @Override
    public String toString() {
        return "Project:\n" +
                "id: " + id +
                "\nProject Name: " + projectName +
                "\nCompany Name: " + companyName + "\n";
    }
}


ProjectComparator.java


package net.proselyte.hibernate.mappings.sortedmap;

import java.util.Comparator;

public class ProjectComparator implements Comparator {
    public int compare(String o1, String o2) {
        final int BEFORE = -1;
        final int AFTER = 1;

      /* To reverse the sorting order, multiple by -1 */
        if (o2 == null) {
            return BEFORE * -1;
        }

        Comparable thisProject = o1;
        Comparable thatProject = o2;

        if(thisProject == null) {
            return AFTER * 1;
        } else if(thatProject == null) {
            return BEFORE * -1;
        } else {
            return thisProject.compareTo(thatProject) * -1;
        }
    }
}


Шаг 3. Создадим конфигурационные файлы

hibernate.cfg.xml


<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.dialect">
            org.hibernate.dialect.MySQLDialect
        </property>
        <property name="hibernate.connection.driver_class">
            com.mysql.jdbc.Driver
        </property>

        <!-- Assume PROSELYTE_TUTORIAL is the database name -->
        <property name="hibernate.connection.url">
            jdbc:mysql://localhost/ИМЯ ВАШЕЙ БД
        </property>
        <property name="hibernate.connection.username">
            ВАШЕ ИМЯ ПОЛЬЗОВАТЕЛЯ
        </property>
        <property name="hibernate.connection.password">
            ВАШ ПАРОЛЬ
        </property>

        <!-- List of XML mapping files -->
        <mapping resource="Developer.hbm.xml"/>

    </session-factory>
</hibernate-configuration>

Developer.hbm.xml


<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="net.proselyte.hibernate.mappings.sortedmap.Developer" table="HIBERNATE_DEVELOPERS">
        <meta attribute="class-description">
            This class contains developer's details.
        </meta>
        <id name="id" type="int" column="id">
            <generator class="native"/>
        </id>
        <map name="projects" cascade="all" sort = "net.proselyte.hibernate.mappings.sortedmap.ProjectComparator">
            <key column="DEVELOPER_ID"/>
            <index column="SPHERE" type="string" />
            <one-to-many class="net.proselyte.hibernate.mappings.sortedmap.Project"/>
        </map>
        <property name="firstName" column="FIRST_NAME" type="string"/>
        <property name="lastName" column="LAST_NAME" type="string"/>
        <property name="specialty" column="SPECIALTY" type="string"/>
        <property name="experience" column="EXPERIENCE" type="int"/>
    </class>

    <class name="net.proselyte.hibernate.mappings.sortedmap.Project" table="HIBERNATE_PROJECTS">
        <meta attribute="class-description">
            This class contains project's records.
        </meta>
        <id name="id" type="int" column="id">
            <generator class="native"/>
        </id>
        <property name="projectName" column="PROJECT_NAME" type="string"/>
        <property name="companyName" column="COMPANY" type="string"/>
    </class>
</hibernate-mapping>

Шаг 4. Создаём класс DeveloperRunner.java

DeveloperRunner.java


package net.proselyte.hibernate.mappings.sortedmap;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

public class DeveloperRunner {
    private static SessionFactory sessionFactory;

    public static void main(String[] args) {
        sessionFactory = new Configuration().configure().buildSessionFactory();

        DeveloperRunner developerRunner = new DeveloperRunner();

        System.out.println("Creating the TreeMaps of projects.");

        TreeMap<String, Project> projects1 = new TreeMap<String, Project>();
        projects1.put("Computer Science", new Project("Proselyte Tutorial", "proselyte.net"));
        projects1.put("Aviation", new Project("SkybleLib", "SkybleSoft"));

        TreeMap<String, Project> projects2 = new TreeMap<String, Project>();
        projects2.put("Some Sphere", new Project("Some Project", "Some Company"));
        projects2.put("E-commerce", new Project("One more Project", "One more Company"));

        System.out.println("Adding developer's records to the DB");

        Integer developerId1 = developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2, projects1);
        Integer developerId2 = developerRunner.addDeveloper("Peter", "UI", "UI Developer", 4, projects2);

        System.out.println("List of developers");
        developerRunner.listDevelopers();

        System.out.println("===================================");
        System.out.println("Updating Proselyte");
        developerRunner.updateDeveloper(developerId1, 3);

        System.out.println("Final list of developers");

        developerRunner.listDevelopers();
        System.out.println("===================================");
        sessionFactory.close();
    }

    public Integer addDeveloper(String firstName, String lastName, String specialty, int experience, TreeMap<String, Project> projects) {
        Session session = sessionFactory.openSession();
        Transaction transaction = null;
        Integer developerId = null;

        transaction = session.beginTransaction();
        Developer developer = new Developer(firstName, lastName, specialty, experience);
        developer.setProjects(projects);
        developerId = (Integer) session.save(developer);
        transaction.commit();
        session.close();
        return developerId;
    }

    public void listDevelopers() {
        Session session = sessionFactory.openSession();
        Transaction transaction = null;

        transaction = session.beginTransaction();
        List developers = session.createQuery("FROM Developer").list();
        for (Developer developer : developers) {
            System.out.println(developer);
            SortedMap<String, Project> projects = developer.getProjects();
            System.out.println(developer.getFirstName() + "'s projects:\n");
            System.out.println(projects);
            System.out.println("\n================\n");
        }
        session.close();
    }

    public void updateDeveloper(int developerId, int experience) {
        Session session = sessionFactory.openSession();
        Transaction transaction = null;

        transaction = session.beginTransaction();
        Developer developer = (Developer) session.get(Developer.class, developerId);
        developer.setExperience(experience);
        session.update(developer);
        transaction.commit();
        session.close();
    }

    public void removeDeveloper(int developerId) {
        Session session = sessionFactory.openSession();
        Transaction transaction = null;

        transaction = session.beginTransaction();
        Developer developer = (Developer) session.get(Developer.class, developerId);
        session.delete(developer);
        transaction.commit();
        session.close();
    }
}


Если всё было сделано правильно, то в результате работы программы мы получилм, примерно, следующий результат:


/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7534 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/3.6.9.Final/hibernate-core-3.6.9.Final.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.6/antlr-2.7.6.jar:/home/proselyte/.m2/repository/commons-collections/commons-collections/3.1/commons-collections-3.1.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-commons-annotations/3.2.0.Final/hibernate-commons-annotations-3.2.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.0-api/1.0.1.Final/hibernate-jpa-2.0-api-1.0.1.Final.jar:/home/proselyte/.m2/repository/javax/transaction/jta/1.1/jta-1.1.jar:/home/proselyte/.m2/repository/org/slf4j/slf4j-api/1.6.1/slf4j-api-1.6.1.jar:/home/proselyte/.m2/repository/javassist/javassist/3.12.1.GA/javassist-3.12.1.GA.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.mappings.sortedmap.DeveloperRunner
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Sat Feb 20 21:45:50 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Creating the TreeMaps of projects.
Adding developer's records to the DB
List of developers
Developer:
id: 81
First Name: Proselyte
Last Name: Developer
Specialty: Java Developer
Experience: 2

Proselyte's projects:

{Computer Science=Project:
id: 26
Project Name: Proselyte Tutorial
Company Name: proselyte.net
, Aviation=Project:
id: 25
Project Name: SkybleLib
Company Name: SkybleSoft
}

================

Developer:
id: 82
First Name: Peter
Last Name: UI
Specialty: UI Developer
Experience: 4

Peter's projects:

{Some Sphere=Project:
id: 28
Project Name: Some Project
Company Name: Some Company
, E-commerce=Project:
id: 27
Project Name: One more Project
Company Name: One more Company
}

================

===================================
Updating Proselyte
Final list of developers
Developer:
id: 81
First Name: Proselyte
Last Name: Developer
Specialty: Java Developer
Experience: 3

Proselyte's projects:

{Computer Science=Project:
id: 26
Project Name: Proselyte Tutorial
Company Name: proselyte.net
, Aviation=Project:
id: 25
Project Name: SkybleLib
Company Name: SkybleSoft
}

================

Developer:
id: 82
First Name: Peter
Last Name: UI
Specialty: UI Developer
Experience: 4

Peter's projects:

{Some Sphere=Project:
id: 28
Project Name: Some Project
Company Name: Some Company
, E-commerce=Project:
id: 27
Project Name: One more Project
Company Name: One more Company
}

================

===================================


В этой статье мы рассмотрели пример связывания с использованием SortedMap.