DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Upgrade Guide To Spring Boot 3.0 for Spring Data JPA and Querydsl
  • Best Performance Practices for Hibernate 5 and Spring Boot 2 (Part 4)
  • Querydsl vs. JPA Criteria, Part 6: Upgrade Guide To Spring Boot 3.2 for Spring Data JPA and Querydsl Project
  • Auditing Spring Boot Using JPA, Hibernate, and Spring Data JPA

Trending

  • Enhancing Security With ZTNA in Hybrid and Multi-Cloud Deployments
  • Automating Data Pipelines: Generating PySpark and SQL Jobs With LLMs in Cloudera
  • The Role of Functional Programming in Modern Software Development
  • AI-Based Threat Detection in Cloud Security
  1. DZone
  2. Coding
  3. Frameworks
  4. How to Use Hibernate Natural IDs in Spring Boot

How to Use Hibernate Natural IDs in Spring Boot

This article is a quick practical guide for using Hibernate Natural IDs (@NaturalId) in Spring Data style.

By 
Anghel Leonard user avatar
Anghel Leonard
DZone Core CORE ·
Feb. 12, 19 · Tutorial
Likes (10)
Comment
Save
Tweet
Share
28.2K Views

Join the DZone community and get the full member experience.

Join For Free

This article is a quick practical guide for using Hibernate Natural IDs (@NaturalId) in Spring Data style. Mainly, we want to expose the Hibernate,bySimpleNaturalId()and byNaturalId()methods via a typical Spring Data repository, and to call them exactly as we call the well-known, findAll(),findOne(), and so on.

Implementation

First, let's focus on the implementation of the needed classes. Having all this in place, we will be able to provide repositories for our entities with natural IDs.

Writing an Entity With Natural ID

Let's consider the following entity that has an auto-generated ID and a natural ID (thecodecolumn). This is just a typical entity using one natural ID via @NaturalId:

@Entity
public class Product implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @NaturalId(mutable = false)
    @Column(nullable = false, updatable = false, unique = true, length = 50)
    private String code;

    // getters and setters

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Product)) {
            return false;
        }
        Product naturalIdProduct = (Product) o;
        return Objects.equals(getCode(), naturalIdProduct.getCode());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getCode());
    }

    @Override
    public String toString() {
        return "Product{" + "id=" + id + ", name=" + name + ", code=" + code + '}';
    }
}

Writing the NaturalRepository Contract

We begin by writing an interface named, NaturalRepository. Basically, when we want to fine-tune a repository, we can rely on @NoRepositoryBeanannotation. In our case, we want to enrich the Spring Data methods arsenal with two more, findBySimpleNaturalId()andfindByNaturalId() :

@NoRepositoryBean
public interface NaturalRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {

    // use this method when your entity has a single field annotated with @NaturalId
    Optional<T> findBySimpleNaturalId(ID naturalId);

    // use this method when your entity has more than one field annotated with @NaturalId
    Optional<T> findByNaturalId(Map<String, Object> naturalIds);        
}

Writing the NaturalRepository Implementation

Further, we extend theSimpleJpaRepository class and implement the NaturalRepository. By extending theSimpleJpaRepositorywe can customize the base repository by adding our needed methods. Mainly, we extend the persistence technology-specific repository base class and use this extension as the custom base class for the repository proxies:

@Transactional(readOnly = true)
public class NaturalRepositoryImpl<T, ID extends Serializable>
        extends SimpleJpaRepository<T, ID> implements NaturalRepository<T, ID> {

    private final EntityManager entityManager;

    public NaturalRepositoryImpl(JpaEntityInformation entityInformation,
            EntityManager entityManager) {
        super(entityInformation, entityManager);

        this.entityManager = entityManager;
    }

    @Override
    public Optional<T> findBySimpleNaturalId(ID naturalId) {

        Optional<T> entity = entityManager.unwrap(Session.class)
                .bySimpleNaturalId(this.getDomainClass())
                .loadOptional(naturalId);

        return entity;
    }

    @Override
    public Optional<T> findByNaturalId(Map<String, Object> naturalIds) {

        NaturalIdLoadAccess<T> loadAccess
                = entityManager.unwrap(Session.class).byNaturalId(this.getDomainClass());
        naturalIds.forEach(loadAccess::using);

        return loadAccess.loadOptional();
    }

}

Setting the NaturalRepositoryImpl as the Base Class

Next, we have to instruct Spring Data to rely on our customized repository base class. In Java configuration, you can do so by using the repositoryBaseClassattribute via the @EnableJpaRepositoriesannotation:

@SpringBootApplication
@EnableJpaRepositories(repositoryBaseClass = NaturalRepositoryImpl.class)
public class NaturalIdApplication {
    ...
}

Let's See How It Works

Now, let's see if it works as expected. First, let's define the ProductRepository:

@Repository
public interface ProductRepository<T, ID> extends NaturalRepository<Product, Long>{    
}

One Natural ID

Further, let's save two products with unique codes (natural ids):

Product tshirt = new Product();
tshirt.setName("T-Shirt");
tshirt.setCode("014-tshirt-2019");

Product socks = new Product();
socks.setName("Socks");
socks.setCode("012-socks-2018");

productRepository.save(tshirt);
productRepository.save(socks);

Finally, let's find the T-Shirt product by its natural ID:

// this should return the t-shirt product wrapped in an Optional
Optional<Product> tshirt = productRepository.findBySimpleNaturalId("014-tshirt-2019");

Note: You can still use productRepository.findById(Object id)if you want to search by the auto-generated id.

More Natural IDs

An entity can use more than one natural ID. For example, let's assume that next to code we have skuas natural ID as well:

@NaturalId(mutable = false)
@Column(nullable = false, updatable = false, unique = true)
private Long sku;

Now, when we persist our product they will look like follows:

Product tshirt = new Product();
tshirt.setName("T-Shirt");
tshirt.setCode("014-tshirt-2019");
tshirt.setSku(1L);

Product socks = new Product();
socks.setName("Socks");
socks.setCode("012-socks-2018");
socks.setSku(2L);

productRepository.save(tshirt);
productRepository.save(socks);

Finding the T-Shirt product can be done like this:

Map<String, Object> ids = new HashMap<>();
ids.put("code", "014-tshirt-2019");
ids.put("sku", 1L);

Optional<Product> tshirt = productRepository.findByNaturalId(ids);

Thanks for reading. Let me know your thoughts in the comments. The source code can be found on GitHub.

Spring Framework Spring Boot Spring Data Hibernate

Opinions expressed by DZone contributors are their own.

Related

  • Upgrade Guide To Spring Boot 3.0 for Spring Data JPA and Querydsl
  • Best Performance Practices for Hibernate 5 and Spring Boot 2 (Part 4)
  • Querydsl vs. JPA Criteria, Part 6: Upgrade Guide To Spring Boot 3.2 for Spring Data JPA and Querydsl Project
  • Auditing Spring Boot Using JPA, Hibernate, and Spring Data JPA

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!