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
Newsletter
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Upgrade Guide To Spring Boot 3.0 for Spring Data JPA and Querydsl
  • 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
  • Spring Boot MapStruct Example of Mapping JPA and Hibernate Entity to DTO | Spring Boot Tutorial

Trending

  • Why Kotlin or Java Is the Wrong Question For Enterprises
  • The Window Closes: Why I Switched to Kotlin Before the Codebase Got Too Big
  • Batching Strategies: Throughput vs Latency in Practice
  • Idempotency Under Concurrency: Why Your Application Check Isn't the Last Line of Defense

Draft This article hasn't been published, so readers can't see it.

In moderation This article is waiting for review, so readers can't see it yet.

Deleted This article has been deleted, so readers can't see it.

  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
DZone Core CORE ·
Feb. 12, 19 · Tutorial
Post on X (opens in a new tab) Share on LinkedIn (opens in a new tab)
29.1K Views

Join the DZone community Save articles, join the discussion, and download Refcards and Trend Reports with a free account.

Join for free Already a member? Log in

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
  • 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
  • Spring Boot MapStruct Example of Mapping JPA and Hibernate Entity to DTO | Spring Boot Tutorial

Partner Resources

×

Comments

Loading comments

The comments didn't load

Please try again in a moment.

Likes

Loading

The next likes didn't load. Please try again.

The likes didn't load

Please try again in a moment.

See who liked this

Log in or join DZone for free to see who liked this article.

Log in Join for free
  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

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 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook