如何在SpringBoot中使用Hibernate @NaturalId?


这是一个Spring Boot应用程序案例,展示如何使用Hibernate映射自然业务键  @NaturalId。

关键点:
1.在实体(例如,Product)中,标记应作为自然ID 的属性(业务键)  @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;

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

对于不可变的id,将列标记为@NaturalId(mutable = false)和@Column(nullable = false, updatable = false, unique = true, ...)

对于可变id,将列标记为@NaturalId(mutable = true)和 @Column(nullable = false, updatable = true, unique = true, ...)

2. 使用自然id覆盖equals()和hashCode()

  @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());
        // including sku 
       
// return Objects.equals(getCode(), naturalIdProduct.getCode())
           
// && Objects.equals(getSku(), naturalIdProduct.getSku());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getCode());
       
// including sku
       
// return Objects.hash(getCode(), getSku());
    }

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

3. 定义一个@NoRepositoryBean接口(例如NaturalRepository)来定义两个名为findBySimpleNaturalId()and的方法findByNaturalId() 


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

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

4. 提供此接口的实现(例如,一个NaturalRepositoryImpl实现  )依赖于Hibernate的Session实现 bySimpleNaturalId(),和  byNaturalId()方法


@Repository
@Transactional(readOnly = true)
public abstract class NaturalRepositoryImpl<T, NID extends Serializable>
        implements NaturalRepository<T, NID> {

    @PersistenceContext
    private EntityManager entityManager;

    private final Class<T> entityClass;

    public NaturalRepositoryImpl(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

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

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

        return entity;
    }

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

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

        return loadAccess.loadOptional();
    }

}

对于实体,编写扩展的存储库类(例如,用于Product实体写入  ProductNaturalRepository)NaturalRepositoryImpl并使用它来设置实体类类型和自然id类型(当实体使用多个自然ID时,类型不再相关,只需将其设置为Serializable)

45 在您的服务中注入此类并调用findBySimpleNaturalId()或 findByNaturalId()


源代码可在此处获得