Spring Boot Composite Primary Key Example

Composite Primary Key

Composite primary key is a combination of two or more columns in a table.

So instead of a single column to be a primary key, more than one column or field is made to be a primary key to uniquely identify the rows in a given table.

Let’s say you have created a table called user in your database and the user table has three columns – id, first name and last name. So you don’t want to make the id column as a primary key but first name and last name together will be a unique and primary key. Here first and last name fields make together a composite primary key.

Prerequisites

Java, Spring Boot/Spring Data JPA, Maven 3.9.8, MySQL 8.1.0

Project Setup

You can use the following pom.xml file for your maven based project.

You can refer to the following pom.xml file for your project:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.roytuts</groupId>
  <artifactId>springboot-data-jpa-composite-primary-key</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.release>22</maven.compiler.release>
	</properties>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>4.0.4</version>
	</parent>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
		</dependency>
		
		<dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-devtools</artifactId>
	        <scope>runtime</scope>
	    </dependency>
	    
	    <dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-starter-test</artifactId>
	        <scope>test</scope>
	        <exclusions>
	            <!-- Exclude vintage engine to avoid JUnit 4 conflicts -->
	            <exclusion>
	                <groupId>org.junit.vintage</groupId>
	                <artifactId>junit-vintage-engine</artifactId>
	            </exclusion>
	        </exclusions>
	    </dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

MySQL Table

The following table can be used for composite primary key example. The first_name and last_name columns make together a composite primary key.

CREATE TABLE user (
    id int unsigned COLLATE utf8mb4_unicode_ci NOT NULL,
    first_name varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL,
    last_name  varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL,    
    PRIMARY KEY (last_name,first_name)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Insert few sample data to test the application once it is built:

INSERT INTO `user` (`id`, `first_name`, `last_name`) VALUES
	(3, 'Arup', 'Roy'),
	(1, 'Soumitra', 'Roy'),
	(2, 'Liton', 'Sarkar');

Composite Primary Key Class

To build a composite primary key class in Java using Spring Boot Data JPA, you need to have the no-argument constructor and you need to override equals() and hashCode() methods for building the logic to check equality.

The composite primary key class can be marked with annotation @Embeddable to use this class as a key in the entity class. Here you need to mention the table’s column names if they are different from Java field name using @Column annotation.

package com.roytuts.springboot.datajpa.composite.primarykey.entity;





@Embeddable
public class UserPKey implements Serializable {

	private static final long serialVersionUID = 1L;

	@Column(name = "first_name")
	private String firstName;

	@Column(name = "last_name")
	private String lastName;

	public UserPKey() {
	}

	public UserPKey(String firstName, String lastName) {
		this.firstName = firstName;
		this.lastName = lastName;
	}

	public String getFirstName() {
		return firstName;
	}

	public String getLastName() {
		return lastName;
	}

	@Override
	public int hashCode() {
		return Objects.hash(getFirstName(), getLastName());
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		UserPKey other = (UserPKey) obj;

		return Objects.equals(getFirstName(), other.getFirstName())
				&& Objects.equals(getLastName(), other.getLastName());
	}

	@Override
	public String toString() {
		return "CompositePKey [firstName=" + firstName + ", lastName=" + lastName + "]";
	}

}

Your composite primary key class must implement Serializable interface otherwise your application will throw the following exception:

Caused by: org.hibernate.MappingException: Composite-id class must implement Serializable: com.roytuts.springboot.datajpa.composite.primarykey.entity.UserPKey
	at org.hibernate.mapping.RootClass.checkCompositeIdentifier

Entity Class

The Java entity class is mapped to the database table (user). Notice in the following entity class I have used @EmbeddedId on the composite primary key class to indicate it as a primary key.

package com.roytuts.springboot.datajpa.composite.primarykey.entity;

@Entity
@Table // (name = "user")
public class User {

	private int id;

	@EmbeddedId
	private UserPKey userPKey;

	public int getId() {
		return id;
	}

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

	@Override
	public String toString() {
		return "User [id=" + id + ", userPKey=" + userPKey + "]";
	}

	public UserPKey getUserPKey() {
		return userPKey;
	}

	public void setUserPKey(UserPKey userPKey) {
		this.userPKey = userPKey;
	}

}

If your Java class name and table name are same then you don’t need to specify the table name using @Table annotation on the entity class.

Repository Interface

Spring Data JPA provides interface which can be used to perform basic CRUD (Create Read Update Delete) operations without writing a single SQL statement.

I am writing a method to find a user using composite primary key value.

package com.roytuts.springboot.datajpa.composite.primarykey.repository;

public interface UserRepository extends JpaRepository<User, UserPKey> {

	User findByUserPKey(UserPKey userPKey);

}

Datasource Configuration

The following standard datasource configuration is written into the file application.properties under src/main/resources folder.

spring.datasource.url=jdbc:mysql://localhost/roytuts
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver

Spring Boot Main Class

The spring boot main class is enough to deploy the application into embedded Tomcat server. Here I am using CLI (Command Line Interface) version of the main class to run the application in standalone mode.

package com.roytuts.springboot.datajpa.composite.primarykey;

@SpringBootApplication
@EntityScan(basePackages = "com.roytuts.springboot.datajpa.composite.primarykey.entity")
@EnableJpaRepositories(basePackages = "com.roytuts.springboot.datajpa.composite.primarykey.repository")
public class App implements CommandLineRunner {





	@Autowired
	private UserRepository userRepository;

	public static void main(String[] args) {
		SpringApplication.run(App.class, args).close();
	}

	@Override
	public void run(String... args) throws Exception {
		System.out.println("=====================================================================");
		List<User> users = userRepository.findAll();
		users.stream().forEach(u -> System.out.println(u));
		System.out.println("---------------------------------------------------------------------");
		System.out.println();

		User user = userRepository.findByUserPKey(new UserPKey("Soumitra", "Roy"));
		System.out.println(user);
		System.out.println("---------------------------------------------------------------------");
		System.out.println();

		User newUser = new User();
		newUser.setId(4);
		newUser.setUserPKey(new UserPKey("First Name", "Last Name"));
		userRepository.save(newUser);

		users = userRepository.findAll();
		users.stream().forEach(u -> System.out.println(u));
		System.out.println("---------------------------------------------------------------------");
		System.out.println();

		newUser.setUserPKey(new UserPKey("First Name", "Last"));
		userRepository.save(newUser);
		
		users = userRepository.findAll();
		users.stream().forEach(u -> System.out.println(u));
		System.out.println("---------------------------------------------------------------------");
		System.out.println();

		userRepository.delete(newUser);

		users = userRepository.findAll();
		users.stream().forEach(u -> System.out.println(u));
		//System.out.println("---------------------------------------------------------------------");
		//System.out.println();
		System.out.println("=====================================================================");
	}

}

Testing Spring Boot Composite Primary Key

By executing the above main class I am testing the example app, where I am fetching existing data, creating a new user data, deleting data.

=====================================================================
User [id=4, userPKey=CompositePKey [firstName=First Name, lastName=Last Name]]
User [id=3, userPKey=CompositePKey [firstName=Arup, lastName=Roy]]
User [id=1, userPKey=CompositePKey [firstName=Soumitra, lastName=Roy]]
User [id=2, userPKey=CompositePKey [firstName=Liton, lastName=Sarkar]]
---------------------------------------------------------------------

User [id=1, userPKey=CompositePKey [firstName=Soumitra, lastName=Roy]]
---------------------------------------------------------------------





User [id=4, userPKey=CompositePKey [firstName=First Name, lastName=Last Name]]
User [id=3, userPKey=CompositePKey [firstName=Arup, lastName=Roy]]
User [id=1, userPKey=CompositePKey [firstName=Soumitra, lastName=Roy]]
User [id=2, userPKey=CompositePKey [firstName=Liton, lastName=Sarkar]]
---------------------------------------------------------------------

User [id=4, userPKey=CompositePKey [firstName=First Name, lastName=Last]]
User [id=4, userPKey=CompositePKey [firstName=First Name, lastName=Last Name]]
User [id=3, userPKey=CompositePKey [firstName=Arup, lastName=Roy]]
User [id=1, userPKey=CompositePKey [firstName=Soumitra, lastName=Roy]]
User [id=2, userPKey=CompositePKey [firstName=Liton, lastName=Sarkar]]
---------------------------------------------------------------------

User [id=4, userPKey=CompositePKey [firstName=First Name, lastName=Last Name]]
User [id=3, userPKey=CompositePKey [firstName=Arup, lastName=Roy]]
User [id=1, userPKey=CompositePKey [firstName=Soumitra, lastName=Roy]]
User [id=2, userPKey=CompositePKey [firstName=Liton, lastName=Sarkar]]
=====================================================================

Hope you got an idea how to create composite primary key in Spring Boot application.

Source Code

Download

Share

Related posts

No comments

Leave a comment