📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.
🎓 Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.
▶️ Subscribe to My YouTube Channel (176K+ subscribers): Java Guides on YouTube
▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube
Hibernate Framework Architecture and Basics Hibernate Hello World Tutorial Hibernate XML Configuration Example Hibernate Java Configuration Example Hibernate Transaction Management Hibernate/JPA - Primary Key Generation Strategies JPA and Hibernate Cascade Types Hibernate - Save an Entity Example Hibernate - Persist an Entity Example Hibernate - saveOrUpdate() Method Example Hibernate - get(), load() and byId() Method Hibernate - merge() Example Hibernate - Delete or Remove an Entity Example Hibernate - load() Method Example Hibernate Session.clear() Method Example Hibernate One to One Mapping Hibernate One to Many Mapping Hibernate Many to Many Annotation Hibernate One to Many CRUD Example Hibernate One to One CRUD Example Hibernate Inheritance Mapping Hibernate Query Language Hibernate CRUD Operations Example Hibernate Session Interface Methods JSP Servlet Hibernate CRUD Example Hibernate Registration Form Example Login Form using JSP + Servlet + Hibernate + MySQL More .........
In this tutorial, we will learn how to implement Hibernate one to one mapping with a shared primary key using @PrimaryKeyJoinColumn annotation.
Hibernate will ensure that it will use a common primary key value in both the tables. This way primary key of Employee can safely be assumed the primary key of Account also.
Employee JPA Entity
Let's create an Employee JPA entity and the account field is annotated with @PrimaryKeyJoinColumn annotation:
package net.javaguides.hibernate.entity.sharedPrimaryKey; import java.io.Serializable; import javax.persistence.CascadeType; 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.PrimaryKeyJoinColumn; import javax.persistence.Table; import javax.persistence.UniqueConstraint; @Entity(name = "SharedPrimaryKeyEmployeeEntity") @Table(name = "Employee", uniqueConstraints = { @UniqueConstraint(columnNames = "ID"), @UniqueConstraint(columnNames = "EMAIL") }) public class Employee implements Serializable { private static final long serialVersionUID = -1798070786993154676 L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ID", unique = true, nullable = false) private Integer employeeId; @Column(name = "EMAIL", unique = true, nullable = false, length = 100) private String email; @Column(name = "FIRST_NAME", unique = false, nullable = false, length = 100) private String firstName; @Column(name = "LAST_NAME", unique = false, nullable = false, length = 100) private String lastName; @OneToOne(cascade = CascadeType.ALL) @PrimaryKeyJoinColumn private Account account; public Integer getEmployeeId() { return employeeId; } public void setEmployeeId(Integer employeeId) { this.employeeId = employeeId; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } 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 Account getAccount() { return account; } public void setAccount(Account account) { this.account = account; } }
Account JPA Entity
On the Account JPA entity side, it will remain dependent on the owner entity (Employee) for the mapping.
package net.javaguides.hibernate.entity.sharedPrimaryKey; import java.io.Serializable; import javax.persistence.CascadeType; 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 javax.persistence.UniqueConstraint; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Parameter; @Entity(name = "SharedPrimaryKeyAccountEntity") @Table(name = "ACCOUNT", uniqueConstraints = { @UniqueConstraint(columnNames = "ID") }) public class Account implements Serializable { private static final long serialVersionUID = -6790693372846798580 L; @Id @Column(name = "ID", unique = true, nullable = false) @GeneratedValue(generator = "gen") @GenericGenerator(name = "gen", strategy = "foreign", parameters = @Parameter(name = "property", value = "employee")) private Integer accountId; @Column(name = "ACC_NUMBER", unique = true, nullable = false, length = 100) private String accountNumber; @OneToOne(mappedBy = "account", cascade = CascadeType.ALL) private Employee employee; public Employee getEmployee() { return employee; } public void setEmployee(Employee employee) { this.employee = employee; } public Integer getAccountId() { return accountId; } public void setAccountId(Integer accountId) { this.accountId = accountId; } public String getAccountNumber() { return accountNumber; } public void setAccountNumber(String accountNumber) { this.accountNumber = accountNumber; } }
Create a Hibernate configuration file - hibernate.cfg.xml
The configuration file contains information about the database and mapping file. Conventionally, its name should be hibernate.cfg.xml.
Let's create an XML file named hibernate.cfg.xml under the resources folder and write the following code in it.
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- JDBC Database connection settings --> <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://localhost:3306/java_demo?useSSL=false</property> <property name="connection.username">root</property> <property name="connection.password">root</property> <!-- JDBC connection pool settings ... using built-in test pool --> <property name="connection.pool_size">1</property> <!-- Select our SQL dialect --> <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property> <!-- Echo the SQL to stdout --> <property name="show_sql">true</property> <!-- Set the current session context --> <property name="current_session_context_class">thread</property> <!-- Drop and re-create the database schema on startup --> <property name="hbm2ddl.auto">create-drop</property> <!-- dbcp connection pool configuration --> <property name="hibernate.dbcp.initialSize">5</property> <property name="hibernate.dbcp.maxTotal">20</property> <property name="hibernate.dbcp.maxIdle">10</property> <property name="hibernate.dbcp.minIdle">5</property> <property name="hibernate.dbcp.maxWaitMillis">-1</property> <!-- <mapping class="net.javaguides.hibernate.entity.foreignKeyAsso.Employee"/><mapping class="net.javaguides.hibernate.entity.foreignKeyAsso.Account"/> --> <mapping class="net.javaguides.hibernate.entity.sharedPrimaryKey.Employee"/> <mapping class="net.javaguides.hibernate.entity.sharedPrimaryKey.Account"/> <!-- <mapping class="net.javaguides.hibernate.entity.joinTable.Employee"/><mapping class="net.javaguides.hibernate.entity.joinTable.Account"/> --> </session-factory> </hibernate-configuration>
Hibernate Utility Class - HibernateUtil.java
Create a helper class to bootstrap hibernate SessionFactory. In most Hibernate applications, the SessionFactory should be instantiated once during application initialization. The single instance should then be used by all code in a particular process, and any Session should be created using this single SessionFactory.
The SessionFactory is thread-safe and can be shared and Session is a single-threaded object.
Let's create a HibernateUtil class to configure singleton SessionFactory and use it throughout the application. The bootstrapping API is quite flexible, but in most cases, it makes the most sense to think of it as a 3 step process:
- Build the StandardServiceRegistry
- Build the Metadata
- Use those 2 to build the SessionFactory
package net.javaguides.hibernate.util; import org.hibernate.SessionFactory; import org.hibernate.boot.Metadata; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; public class HibernateUtil { private static StandardServiceRegistry registry; private static SessionFactory sessionFactory; public static SessionFactory getSessionFactory() { if (sessionFactory == null) { try { // Create registry registry = new StandardServiceRegistryBuilder().configure().build(); // Create MetadataSources MetadataSources sources = new MetadataSources(registry); // Create Metadata Metadata metadata = sources.getMetadataBuilder().build(); // Create SessionFactory sessionFactory = metadata.getSessionFactoryBuilder().build(); } catch (Exception e) { e.printStackTrace(); if (registry != null) { StandardServiceRegistryBuilder.destroy(registry); } } } return sessionFactory; } public static void shutdown() { if (registry != null) { StandardServiceRegistryBuilder.destroy(registry); } } }
Create the Test class and Run an Application
package net.javaguides.hibernate.test; import org.hibernate.Session; import net.javaguides.hibernate.entity.sharedPrimaryKey.Account; import net.javaguides.hibernate.entity.sharedPrimaryKey.Employee; import net.javaguides.hibernate.util.HibernateUtil; public class TestSharedPrimaryKey { public static void main(String[] args) { Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); Account account = new Account(); account.setAccountNumber("123-345-65454"); //Add new Employee object Employee emp = new Employee(); emp.setEmail("demo-user@mail.com"); emp.setFirstName("demo"); emp.setLastName("user"); emp.setAccount(account); account.setEmployee(emp); //Save Employee session.save(emp); session.getTransaction().commit(); HibernateUtil.shutdown(); } }
Output
The above application will generate the following output:
Dec 27, 2019 7:14:29 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.3.7.Final} Dec 27, 2019 7:14:29 PM org.hibernate.cfg.Environment <clinit> INFO: HHH000206: hibernate.properties not found Dec 27, 2019 7:14:29 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager <clinit> INFO: HCANN000001: Hibernate Commons Annotations {5.0.4.Final} Dec 27, 2019 7:14:30 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Dec 27, 2019 7:14:30 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.cj.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/java_demo?useSSL=false] Dec 27, 2019 7:14:30 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Dec 27, 2019 7:14:30 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Dec 27, 2019 7:14:30 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init> INFO: HHH000115: Hibernate connection pool size: 1 (min=1) Dec 27, 2019 7:14:30 PM org.hibernate.dialect.Dialect <init> INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect Hibernate: drop table if exists ACCOUNT Dec 27, 2019 7:14:31 PM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@10b3df93] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode. Hibernate: drop table if exists Employee Hibernate: create table ACCOUNT (ID integer not null, ACC_NUMBER varchar(100) not null, primary key (ID)) engine=MyISAM Dec 27, 2019 7:14:31 PM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@12f3afb5] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode. Hibernate: create table Employee (ID integer not null auto_increment, EMAIL varchar(100) not null, FIRST_NAME varchar(100) not null, LAST_NAME varchar(100) not null, primary key (ID)) engine=MyISAM Hibernate: alter table ACCOUNT add constraint UK_nsa1j7vica4ow9xhhkudukb3j unique (ACC_NUMBER) Hibernate: alter table Employee add constraint UK_ardf0f11mfa6tujs3hflthwdv unique (EMAIL) Dec 27, 2019 7:14:32 PM org.hibernate.tool.schema.internal.SchemaCreatorImpl applyImportSources INFO: HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@7bd69e82' Hibernate: insert into Employee (EMAIL, FIRST_NAME, LAST_NAME) values (?, ?, ?) Hibernate: insert into ACCOUNT (ACC_NUMBER, ID) values (?, ?) Dec 27, 2019 7:14:32 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost:3306/java_demo?useSSL=false]
Comments
Post a Comment
Leave Comment