Руководство по Hibernate. Виды связей. One-to-One.

В том случае, если поле одного из объектов содержит один единственный экземпляр другого объекта имеет смысл применить тип ассоциации One-to-One.

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

Пример:

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

Шаг 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,
   CONTACT INT DEFAULT NULL,
   PRIMARY KEY(ID)
);

HIBERNATE_CONTACTS


CREATE TABLE HIBERNATE_CONTACTS (
   ID INT NOT NULL auto_increment,
   ADDRESS VARCHAR(100) default NULL,
   CITY VARCHAR(50) default NULL,
   PHONE_NUMBER VARCHAR(20) default NULL,
   EMAIL VARCHAR(100) default NULL,
   PRIMARY KEY (id)
);

Шаг 2. Создаём POJO-классы

Developer.java


package net.proselyte.hibernate.association.onetoone;

public class Developer {
    private int id;
    private String firstName;
    private String lastName;
    private String specialty;
    private int experience;
    private Contact contact;

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

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


    /**
     * 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 Contact getContact() {
        return contact;
    }

    public void setContact(Contact contact) {
        this.contact = contact;
    }

    /**
     * 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" +
                "Contact Information:\n" + contact;
    }
}


Contact.java


package net.proselyte.hibernate.association.onetoone;

public class Contact {
    private int id;
    private String address;
    private String city;
    private String phoneNumber;
    private String email;

    public Contact() {
    }

    public Contact(String address, String city, String phoneNumber, String email) {
        this.address = address;
        this.city = city;
        this.phoneNumber = phoneNumber;
        this.email = email;
    }

    public int getId() {
        return id;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "Address: " + address +
                "\nCity: " + city +
                "\nPhone Number: " + phoneNumber +
                "\nemail: " + email + "\n";
    }
}


Шаг 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.association.onetoone.Developer" table="HIBERNATE_DEVELOPERS">
        <meta attribute="class-description">
            This class contains developer's detail.
        </meta>
        <id name="id" type="int" column="id">
            <generator class="native"/>
        </id>
        <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"/>
        <many-to-one name="contact" column="CONTACT" not-null="true" unique="true"
                    class="net.proselyte.hibernate.association.onetoone.Contact" />
    </class>

    <class name="net.proselyte.hibernate.association.onetoone.Contact" table="HIBERNATE_CONTACTS">
        <meta attribute="class-description">
            This class contains the address detail.
        </meta>
        <id name="id" type="int" column="ID">
            <generator class="native"/>
        </id>
        <property name="address" column="ADDRESS" type="string"/>
        <property name="city" column="CITY" type="string"/>
        <property name="phoneNumber" column="PHONE_NUMBER" type="string"/>
        <property name="email" column="EMAIL" type="string"/>
    </class>

</hibernate-mapping>

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

DeveloperRunner.java


package net.proselyte.hibernate.association.onetoone;

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

import java.util.List;


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("Adding contact information...");
        Contact contact1 = developerRunner.addContact("Some address", "Dnipropetrovsk", "+380501234567", "proselytear@yahoo.com");
        Contact contact2 = developerRunner.addContact("One More address", "Kyiv", "+380667654321", "peter@mail.com");

        System.out.println("Creating developer's records...");
        Integer developerId1 = developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2, contact1);
        Integer developerId2 = developerRunner.addDeveloper("Peter", "Programmer", "C++ Developer", 2, contact2);

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

        System.out.println("Updating experience of Proselyte to 3 years and removing Peter...");
        developerRunner.updateDeveloper(developerId1, 3);
        developerRunner.removeDeveloper(developerId2);

        System.out.println("Final list of Developers: ");
        developerRunner.listDevelopers();

        sessionFactory.close();
    }

    public Integer addDeveloper(String firstName, String lastName, String specialty, int experience, Contact contact) {
        Session session = sessionFactory.openSession();
        Transaction transaction = null;
        Integer developerId = null;

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

    public Contact addContact(String address, String city, String phoneNumber, String email) {
        Session session = sessionFactory.openSession();
        Transaction transaction = null;

        transaction = session.beginTransaction();
        Contact contact = new Contact(address, city, phoneNumber, email);
        session.save(contact);
        transaction.commit();
        session.close();

        return contact;
    }

    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);
            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=7540 -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/5.1.0.Final/hibernate-core-5.1.0.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/com/fasterxml/classmate/1.3.0/classmate-1.3.0.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.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.association.onetoone.DeveloperRunner
Feb 21, 2016 7:02:14 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {5.1.0.Final}
Feb 21, 2016 7:02:14 PM org.hibernate.cfg.Environment 
INFO: HHH000206: hibernate.properties not found
Feb 21, 2016 7:02:14 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Feb 21, 2016 7:02:15 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager 
INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
Feb 21, 2016 7:02:16 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!)
Feb 21, 2016 7:02:16 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL]
Feb 21, 2016 7:02:16 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {user=root, password=****}
Feb 21, 2016 7:02:16 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
Feb 21, 2016 7:02:16 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections 
INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
Sun Feb 21 19:02:16 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.
Feb 21, 2016 7:02:16 PM org.hibernate.dialect.Dialect 
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
Adding contact information...
Creating developer's records...
List of Developers: 
Feb 21, 2016 7:02:17 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService
INFO: HHH000397: Using ASTQueryTranslatorFactory
Developer:
id: 11
First Name: Proselyte
Last Name: Developer
Specialty: Java Developer
Experience: 2
Contact Information:
Address: Some address
City: Dnipropetrovsk
Phone Number: +380501234567
email: proselytear@yahoo.com


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

Developer:
id: 12
First Name: Peter
Last Name: Programmer
Specialty: C++ Developer
Experience: 2
Contact Information:
Address: One More address
City: Kyiv
Phone Number: +380667654321
email: peter@mail.com


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

Updating experience of Proselyte to 3 years and removing Peter...
Final list of Developers: 
Developer:
id: 11
First Name: Proselyte
Last Name: Developer
Specialty: Java Developer
Experience: 3
Contact Information:
Address: Some address
City: Dnipropetrovsk
Phone Number: +380501234567
email: proselytear@yahoo.com


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

Feb 21, 2016 7:02:17 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost/PROSELYTE_TUTORIAL]


В этой статье мы рассмотрели пример испльзования связей (mapping) с помощью ассоциации One-to-One.