Hibernate 一对一映射示例注释
今天我们将研究 Hibernate 中的一对一映射。我们将使用注释和 XML 配置研究 Hibernate 一对一映射示例。
Hibernate 中的一对一映射
大多数情况下,数据库表是相互关联的。关联形式有很多种 -广义上是一对一、一对多和多对多。这些可以进一步分为单向和双向映射。今天,我们将研究使用XML 配置以及使用注释配置实现Hibernate 一对一映射。
Hibernate 一对一映射示例数据库设置
首先,我们需要在数据库表中设置一对一映射。我们将为我们的示例创建两个表 - Transaction 和 Customer。这两个表都将具有一对一映射。Transaction 将是主表,我们将使用Customer 表中的外键进行一对一映射。我提供 MySQL 脚本,这是我在本教程中使用的数据库。如果您使用任何其他数据库,请确保相应地更改脚本。
-- Create Transaction Table
CREATE TABLE `Transaction` (
`txn_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`txn_date` date NOT NULL,
`txn_total` decimal(10,0) NOT NULL,
PRIMARY KEY (`txn_id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;
-- Create Customer table
CREATE TABLE `Customer` (
`txn_id` int(11) unsigned NOT NULL,
`cust_name` varchar(20) NOT NULL DEFAULT '',
`cust_email` varchar(20) DEFAULT NULL,
`cust_address` varchar(50) NOT NULL DEFAULT '',
PRIMARY KEY (`txn_id`),
CONSTRAINT `customer_ibfk_1` FOREIGN KEY (`txn_id`) REFERENCES `Transaction` (`txn_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
上述表之间一对一映射的实体关系图 (ERD) 如下图所示。我们的数据库设置已准备就绪,现在让我们继续 Hibernate 一对一示例项目。
Hibernate 一对一映射示例项目结构
在 Java IDE 中创建一个简单的 Maven 项目,我使用的是 Eclipse。我们的最终项目结构如下图所示。首先,我们将研究基于 XML 的 Hibernate 一对一映射示例,然后我们将使用注释实现相同的操作。
Hibernate Maven 依赖项
我们的最终 pom.xml 文件如下所示。
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev.hibernate</groupId>
<artifactId>HibernateOneToOneMapping</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.5.Final</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.5</version>
</dependency>
</dependencies>
</project>
依赖项仅适用于 Hibernate 和 mysql java 驱动程序。请注意,我使用的是 Hibernate 最新版本4.3.5.Final和基于我的 MySQL 数据库服务器版本 (5.0.5) 的 MySQL java 驱动程序。Hibernate 4 使用 JBoss 日志记录,它会作为传递依赖项自动导入。您可以在项目的 maven 依赖项中确认这一点。如果您使用的是 Hibernate 旧版本,则可能必须添加 slf4j 依赖项。
Hibernate 一对一映射模型类
用于反映数据库表的 Hibernate 一对一映射的模型类如下所示。
package com.journaldev.hibernate.model;
import java.util.Date;
public class Txn {
private long id;
private Date date;
private double total;
private Customer customer;
@Override
public String toString(){
return id+", "+total+", "+customer.getName()+", "+customer.getEmail()+", "+customer.getAddress();
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public double getTotal() {
return total;
}
public void setTotal(double total) {
this.total = total;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
package com.journaldev.hibernate.model;
public class Customer {
private long id;
private String name;
private String email;
private String address;
private Txn txn;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Txn getTxn() {
return txn;
}
public void setTxn(Txn txn) {
this.txn = txn;
}
}
由于我们使用基于 XML 的配置进行映射,因此上述模型类是简单的 POJO 类或具有 getter-setter 方法的 Java Bean。我使用类名以Txn
避免混淆,因为 Hibernate API 的类名为Transaction
。
Hibernate 一对一映射配置
让我们为Txn和Customer表创建Hibernate一对一映射配置文件。txn.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"https://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.journaldev.hibernate.model.Txn" table="TRANSACTION" >
<id name="id" type="long">
<column name="txn_id" />
<generator class="identity" />
</id>
<property name="date" type="date">
<column name="txn_date" />
</property>
<property name="total" type="double">
<column name="txn_total" />
</property>
<one-to-one name="customer" class="com.journaldev.hibernate.model.Customer"
cascade="save-update" />
</class>
</hibernate-mapping>
The important point to note above is the hibernate one-to-one
element for customer property. customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"https://hibernate.org/dtd/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class name="com.journaldev.hibernate.model.Customer" table="CUSTOMER">
<id name="id" type="long">
<column name="txn_id" />
<generator class="foreign">
<param name="property">txn</param>
</generator>
</id>
<one-to-one name="txn" class="com.journaldev.hibernate.model.Txn"
constrained="true"></one-to-one>
<property name="name" type="string">
<column name="cust_name"></column>
</property>
<property name="email" type="string">
<column name="cust_email"></column>
</property>
<property name="address" type="string">
<column name="cust_address"></column>
</property>
</class>
</hibernate-mapping>
generator class=“foreign” is the important part that is used for hibernate foreign key implementation.
Hibernate Configuration File
Here is the hibernate configuration file for XML based hibernate mapping configuration. hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.password">pankaj123</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
<property name="hibernate.connection.username">pankaj</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.show_sql">true</property>
<mapping resource="txn.hbm.xml"/>
<mapping resource="customer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Hibernate configuration file is simple, it has database connection properties and hibernate mapping resources.
Hibernate SessionFactory Utility
Here is the utility class to create hibernate SessionFactory instance.
package com.journaldev.hibernate.util;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
public class HibernateUtil {
private static SessionFactory sessionFactory;
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
Configuration configuration = new Configuration();
configuration.configure("hibernate.cfg.xml");
System.out.println("Hibernate Configuration loaded");
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
System.out.println("Hibernate serviceRegistry created");
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
ex.printStackTrace();
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
if(sessionFactory == null) sessionFactory = buildSessionFactory();
return sessionFactory;
}
}
That’s it, lets write a test program to test the hibernate one to one mapping xml based configuration.
Hibernate One to One Mapping XML Configuration Test Program
In the hibernate one to one mapping example test program, first we will create Txn object and save it. Once it’s saved into database, we will use the generated id to retrieve the Txn object and print it.
package com.journaldev.hibernate.main;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Customer;
import com.journaldev.hibernate.model.Txn;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernateOneToOneMain {
public static void main(String[] args) {
Txn txn = buildDemoTransaction();
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try{
//Get Session
sessionFactory = HibernateUtil.getSessionFactory();
session = sessionFactory.getCurrentSession();
System.out.println("Session created");
//start transaction
tx = session.beginTransaction();
//Save the Model object
session.save(txn);
//Commit transaction
tx.commit();
System.out.println("Transaction ID="+txn.getId());
//Get Saved Trasaction Data
printTransactionData(txn.getId(), sessionFactory);
}catch(Exception e){
System.out.println("Exception occured. "+e.getMessage());
e.printStackTrace();
}finally{
if(!sessionFactory.isClosed()){
System.out.println("Closing SessionFactory");
sessionFactory.close();
}
}
}
private static void printTransactionData(long id, SessionFactory sessionFactory) {
Session session = null;
Transaction tx = null;
try{
//Get Session
sessionFactory = HibernateUtil.getSessionFactory();
session = sessionFactory.getCurrentSession();
//start transaction
tx = session.beginTransaction();
//Save the Model object
Txn txn = (Txn) session.get(Txn.class, id);
//Commit transaction
tx.commit();
System.out.println("Transaction Details=\n"+txn);
}catch(Exception e){
System.out.println("Exception occured. "+e.getMessage());
e.printStackTrace();
}
}
private static Txn buildDemoTransaction() {
Txn txn = new Txn();
txn.setDate(new Date());
txn.setTotal(100);
Customer cust = new Customer();
cust.setAddress("Bangalore, India");
cust.setEmail("pankaj@gmail.com");
cust.setName("Pankaj Kumar");
txn.setCustomer(cust);
cust.setTxn(txn);
return txn;
}
}
Now when we run above one to one mapping in hibernate test program, we get following output.
Hibernate Configuration loaded
Hibernate serviceRegistry created
Session created
Hibernate: insert into TRANSACTION (txn_date, txn_total) values (?, ?)
Hibernate: insert into CUSTOMER (cust_name, cust_email, cust_address, txn_id) values (?, ?, ?, ?)
Transaction ID=19
Hibernate: select txn0_.txn_id as txn_id1_1_0_, txn0_.txn_date as txn_date2_1_0_, txn0_.txn_total as txn_tota3_1_0_,
customer1_.txn_id as txn_id1_0_1_, customer1_.cust_name as cust_nam2_0_1_, customer1_.cust_email as cust_ema3_0_1_,
customer1_.cust_address as cust_add4_0_1_ from TRANSACTION txn0_ left outer join CUSTOMER customer1_ on
txn0_.txn_id=customer1_.txn_id where txn0_.txn_id=?
Transaction Details=
19, 100.0, Pankaj Kumar, pankaj@gmail.com, Bangalore, India
Closing SessionFactory
As you can see that it’s working fine and we are able to retrieve data from both the tables using transaction id. Check the SQL used by Hibernate internally to get the data, its using joins to get the data from both the tables.
Hibernate One to One Mapping Annotation
In the above section, we saw how to use XML based configuration for hibernate one to one mapping, now let’s see how we can use JPA and Hibernate annotation to achieve the same thing.
Hibernate Configuration File
hibernate-annotation.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.password">pankaj123</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
<property name="hibernate.connection.username">pankaj</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.show_sql">true</property>
<mapping class="com.journaldev.hibernate.model.Txn1"/>
<mapping class="com.journaldev.hibernate.model.Customer1"/>
</session-factory>
</hibernate-configuration>
Hibernate configuration is simple, as you can see that I have two model classes that we will use with annotations - Txn1
and Customer1
.
Hibernate One to One Mapping Annotation Example Model Classes
For hibernate one to one mapping annotation configuration, model classes are the most important part. Let’s see how our model classes look.
package com.journaldev.hibernate.model;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Cascade;
@Entity
@Table(name="TRANSACTION")
public class Txn1 {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="txn_id")
private long id;
@Column(name="txn_date")
private Date date;
@Column(name="txn_total")
private double total;
@OneToOne(mappedBy="txn")
@Cascade(value=org.hibernate.annotations.CascadeType.SAVE_UPDATE)
private Customer1 customer;
@Override
public String toString(){
return id+", "+total+", "+customer.getName()+", "+customer.getEmail()+", "+customer.getAddress();
}
//Getter-Setter methods, omitted for clarity
}
Notice that most of the annotations are from Java Persistence API because Hibernate provide it’s implementation. However for cascading, we would need to use Hibernate annotation org.hibernate.annotations.Cascade
and enum org.hibernate.annotations.CascadeType
.
package com.journaldev.hibernate.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
@Entity
@Table(name="CUSTOMER")
public class Customer1 {
@Id
@Column(name="txn_id", unique=true, nullable=false)
@GeneratedValue(generator="gen")
@GenericGenerator(name="gen", strategy="foreign", parameters={@Parameter(name="property", value="txn")})
private long id;
@Column(name="cust_name")
private String name;
@Column(name="cust_email")
private String email;
@Column(name="cust_address")
private String address;
@OneToOne
@PrimaryKeyJoinColumn
private Txn1 txn;
//Getter-Setter methods
}
Note that we would need to @GenericGenerator
so that id is used from the txn rather than generating it.
Hibernate SessionFactory Utility class
Creating SessionFactory is independent of the way we provide hibernate mapping. Our utility class for creating SessionFactory looks like below.
package com.journaldev.hibernate.util;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
public class HibernateAnnotationUtil {
private static SessionFactory sessionFactory;
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate-annotation.cfg.xml
Configuration configuration = new Configuration();
configuration.configure("hibernate-annotation.cfg.xml");
System.out.println("Hibernate Annotation Configuration loaded");
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
System.out.println("Hibernate Annotation serviceRegistry created");
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
ex.printStackTrace();
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
if(sessionFactory == null) sessionFactory = buildSessionFactory();
return sessionFactory;
}
}
Hibernate One to One Mapping Annotation Example Test Program
Here is a simple test program for our hibernate one to one mapping annotation example.
package com.journaldev.hibernate.main;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Customer1;
import com.journaldev.hibernate.model.Txn1;
import com.journaldev.hibernate.util.HibernateAnnotationUtil;
public class HibernateOneToOneAnnotationMain {
public static void main(String[] args) {
Txn1 txn = buildDemoTransaction();
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try{
//Get Session
sessionFactory = HibernateAnnotationUtil.getSessionFactory();
session = sessionFactory.getCurrentSession();
System.out.println("Session created using annotations configuration");
//start transaction
tx = session.beginTransaction();
//Save the Model object
session.save(txn);
//Commit transaction
tx.commit();
System.out.println("Annotation Example. Transaction ID="+txn.getId());
//Get Saved Trasaction Data
printTransactionData(txn.getId(), sessionFactory);
}catch(Exception e){
System.out.println("Exception occured. "+e.getMessage());
e.printStackTrace();
}finally{
if(sessionFactory != null && !sessionFactory.isClosed()){
System.out.println("Closing SessionFactory");
sessionFactory.close();
}
}
}
private static void printTransactionData(long id, SessionFactory sessionFactory) {
Session session = null;
Transaction tx = null;
try{
//Get Session
sessionFactory = HibernateAnnotationUtil.getSessionFactory();
session = sessionFactory.getCurrentSession();
//start transaction
tx = session.beginTransaction();
//Save the Model object
Txn1 txn = (Txn1) session.get(Txn1.class, id);
//Commit transaction
tx.commit();
System.out.println("Annotation Example. Transaction Details=\n"+txn);
}catch(Exception e){
System.out.println("Exception occured. "+e.getMessage());
e.printStackTrace();
}
}
private static Txn1 buildDemoTransaction() {
Txn1 txn = new Txn1();
txn.setDate(new Date());
txn.setTotal(100);
Customer1 cust = new Customer1();
cust.setAddress("San Jose, USA");
cust.setEmail("pankaj@yahoo.com");
cust.setName("Pankaj Kr");
txn.setCustomer(cust);
cust.setTxn(txn);
return txn;
}
}
Here is the output snippet when we execute above program.
Hibernate Annotation Configuration loaded
Hibernate Annotation serviceRegistry created
Session created using annotations configuration
Hibernate: insert into TRANSACTION (txn_date, txn_total) values (?, ?)
Hibernate: insert into CUSTOMER (cust_address, cust_email, cust_name, txn_id) values (?, ?, ?, ?)
Annotation Example. Transaction ID=20
Hibernate: select txn1x0_.txn_id as txn_id1_1_0_, txn1x0_.txn_date as txn_date2_1_0_, txn1x0_.txn_total as txn_tota3_1_0_,
customer1x1_.txn_id as txn_id1_0_1_, customer1x1_.cust_address as cust_add2_0_1_, customer1x1_.cust_email as cust_ema3_0_1_,
customer1x1_.cust_name as cust_nam4_0_1_ from TRANSACTION txn1x0_ left outer join CUSTOMER customer1x1_ on
txn1x0_.txn_id=customer1x1_.txn_id where txn1x0_.txn_id=?
Annotation Example. Transaction Details=
20, 100.0, Pankaj Kr, pankaj@yahoo.com, San Jose, USA
Closing SessionFactory
Notice that the output is similar to hibernate one to one XML based configuration. That’s all for Hibernate One to One mapping example, you can download the final project from below link and play around with it to learn more.
Download Hibernate OneToOne Mapping Project