- JPA Buddy
Features
- Sophisticated support to build repositories based on Spring and JPA
- Support for Querydsl predicates and thus type-safe JPA queries
- Transparent auditing of domain class
- Pagination support, dynamic query execution, ability to integrate custom data access code
- Validation of @Query annotated queries at bootstrap time
- Support for XML based entity mapping
- JavaConfig based repository configuration by introducing @EnableJpaRepositories.
Annotations
- Spring Data JPA 中常用注解
[x] @Embedded和@Embeddable注解 两个注解同时使用的那部分还没看,一般用不到
@Column
@Column的默认值就是字段名,不会自动转下划线,如果是驼峰字段的话,需要显示定义name。
@Entity@Data@Table(name = "book")public class Book extends BaseEntity implements Serializable {private static final long serialVersionUID = 1L;@NotEmpty(message = "书名不能为空")private String name;@DateTimeFormat(pattern = "yyyy-MM-dd")@Column(name = "publish_date")private Date publishDate;@Enumerated(EnumType.STRING)private BookStatus status;}
@DateTimeFormat
@Enumerated
@Where
@Access
- AccessType.PROPERTY: The JPA persistence implementation will load state into your class via JavaBean “setter” methods, and retrieve state from your class using JavaBean “getter” methods. This is the default.
AccessType.FIELD: State is loaded and retrieved directly from your class’ fields. You do not have to write JavaBean “getters” and “setters”
@MappedSuperclass
在Jpa里, 当我们在定义多个实体类时, 可能会遇到这几个实体类都有几个共同的属性, 这时就会出现很多重复代码.
这时我们可以选择编写一个父类,将这些共同属性放到这个父类中, 并且在父类上加上@MappedSuperclass注解.
注意:标注为@MappedSuperclass的类将不是一个完整的实体类,他将不会映射到数据库表,但是他的属性都将映射到其子类的数据库字段中。
- 标注为@MappedSuperclass的类不能再标注@Entity或@Table注解,也无需实现序列化接口。
@JsonIgnoreProperties
When you have JPA / Hibernate Entities with @Entity annotation, and when you fetch data from the database using a repository or using getMethod() from the parent entity for the field which is being lazy-loaded from the parent entity, hibernate returns an object which will have all the fields/properties of the class which are mapped to DB table. On top of these fields, this object will also have two extra fields which are hibernateLazyInitializer and handler that is used to lazily load an entity. If you have any use case of serializing this entity in JSON String format using Jackson library directly or indirectly (Maybe if you’re returning entity as it to any REST API response or if you’re storing entity to JSON data store like Elasticsearch), the JPA entity will be serialized with all the fields and hibernateLazyInitializer and handler as extra fields. So, if you do not ignore these fields, they will be serialized in JSON format which you can see if you read the JSON string. So, to avoid this unnecessary serialization, you have to write this piece of code on JPA / Hibernate entity which will tell Jackson library that “Serialized JSON should not have fields hibernateLazyInitializer and handler. If you find them in object, just ignore them”:
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
oneToMany和ManyToMany字段防止死循环
@Entity@Datapublic class Permission {@Id@GeneratedValueprivate Long id;private String name;@JsonIgnoreProperties(value = {"permissions"})@ManyToMany@JoinTable(name = "role_permission",joinColumns = {@JoinColumn(name = "permission_id")},inverseJoinColumns = {@JoinColumn(name = "role_id")})private List<Role> roles;}
@UpdateTimestamp
Marks a property as the update timestamp of the containing entity. The property value will be set to the current VM date whenever the owning entity is updated.
orphanRemoval
How does orphanRemoval work with JPA and Hibernate
@OneToMany中
如果orphanRemoval=true,从一的一方对应的collection中remove多的一方的一项,则会从数据库delete child;如果orphanRemoval=false,则只会update parent_id为null
insertable
@Column和@JoinColumn中,insertable属性表示在使用“INSERT”脚本插入数据时,是否需要插入该字段的值。
updatable
@Column和@JoinColumn中,updatable属性表示在使用“UPDATE”脚本插入数据时,是否需要更新该字段的值。insertable和updatable属性一般多用于只读的属性,例如主键和外键等。这些字段的值通常是自动生成的。
referencedColumnName
@DynamicInsert
@DynamicUpdate
DynamicUpdate with Spring Data JPA
@EntityListeners 审计
Spring JPA 使用@CreatedDate、@CreatedBy、@LastModifiedDate、@LastModifiedBy 自动生成时间和修改者
JpaRepository
复杂业务,慎用Spring Data Jpa的findBy…,是一种反模式。 增删改:类比为Collection 查:语义化的Specification接口实现,
AccountSpecificationByUserName,AccountSpecificationByAgeRange
package com.thinkinginobjects.repository;import java.util.List;import com.thinkinginobjects.domainobject.Account;public interface AccountRepository {void addAccount(Account account);void removeAccount(Account account);void updateAccount(Account account); // Think it as replace for setList query(AccountSpecification specification);}
package com.thinkinginobjects.specification;import org.hibernate.criterion.Criterion;import org.hibernate.criterion.Restrictions;import com.thinkinginobjects.domainobject.Account;import com.thinkinginobjects.repository.AccountSpecification;import com.thinkinginobjects.repository.HibernateSpecification;public class AccountSpecificationByUserName implements AccountSpecification, HibernateSpecification {private String desiredUserName;public AccountSpecificationByUserName(String desiredUserName) {super();this.desiredUserName = desiredUserName;}@Overridepublic boolean specified(Account account) {return account.hasUseName(desiredUserName);}@Overridepublic Criterion toCriteria() {return Restrictions.eq("userName", desiredUserName);}}package com.thinkinginobjects.specification;import com.thinkinginobjects.domainobject.Account;import com.thinkinginobjects.repository.AccountSpecification;import com.thinkinginobjects.repository.SqlSpecification;public class AccountSpecificationByAgeRange implements AccountSpecification, SqlSpecification{private int minAge;private int maxAge;public AccountSpecificationByAgeRange(int minAge, int maxAge) {super();this.minAge = minAge;this.maxAge = maxAge;}@Overridepublic boolean specified(Account account) {return account.ageBetween(minAge, maxAge);}@Overridepublic String toSqlClauses() {return String.format("age between %s and %s", minAge, maxAge);}}
简单查询
@Repositorypublic interface demoRepository extends JapRepository<Book, Integer> {// 最好都返回List<T>或Optional<List<T>>,不返回T,因为不确保查到的是唯一的public List<Customer> findByCustNameAndCustIndustry(String name, String industry);public Optional<List<Customer>> findByCustIndustry(String industry);Page<Book> findAllByNameLike(String name, Pageable pageable);Page<Book> findAllByIsbn(String isbn, Pageable pageable);@Query(value = "select * from Customer where custName = ?1 and custIndustry = ?2")List<Customer> nativeFindByCustNameAndCustIndustry(String name, String industry);@Query(value = "select * from cst_customer where cust_name = ?1 and cust_industry = ?2", nativeQuery = true)List<Customer> nativeFindByCustNameAndCustIndustry(String name, String industry);@Query(value = "update Customer set custName = ?2 where custId = ?1")@Modifyingvoid updateCustomer(long id, String name);}
Difference Between save() and saveAndFlush() in Spring Data JPA
saveAndFlush:Normally, we use this method when our business logic needs to read the saved changesat a later point during the same transaction but before the commit.
@Query
- SpringData JPA @Query 注解实现自定义查询方法
-
JpaSpecificationExecutor动态查询
查询部分字段
public interface OrderDemoRepository extends PagingAndSortingRepository<Order, Integer> {@Query("select new com.yanjing.OrderDo(oh.id.orderNo, oh.id.orderType, oh.entryDatetime) from Order oh where oh.id.orderNo in (:orderNos) and oh.entryDatetime >= :fromDatetime ")List<OrderDo> listByIdAndEntryDateTime(@Param("orderNos") List<Integer> orderNos,@Param("fromDatetime") Timestamp fromDatetime);}
配置
SpringBoot的配置
spring:datasource:url: jdbc:mysql://localhost:3306/springboot?allowPublicKeyRetrieval=true&useSSL=false&&serverTimezone=Asia/Shanghaiusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver# 被sql.init.mode替代# initialization-mode: alwaysjpa:show-sql: truehibernate:ddl-auto: updatesql:init:mode: alwaysschema-locations: classpath*:schema-all.sqldata-locations: classpath*:data-all.sql
Spring的配置
Spring Data Jpa的配置:
- dataSource配置数据库连接池
- 配置entityManagerFactory,创建entityManagerFactory对象交给Spring容器管理
- 事务管理器,整合Spring Data Jpa (JpaTransactionManager)
- txAdvice + aop:config
- context:component-scan配置包扫描
Spring Data Jpa视频笔记
- 延迟加载:entityManagergetReference, jpaRepository.getOne
- SpringBoot用spring-boot-starter-data-jpa即可,里面有aop、hibernate等
- Spring的配置,了解,知道是什么;SpringBoot简化了配置
- JpaRepository源碼分析:
- 通过debug方式看源码
- 通过JDKDynamicAopProxy(实现InvocationHandler)创建动态代理对象
- 动态代理类:SimpleJpaRepository

- 更新 @Query+@Modifying
- Service层需要添加@Transactional(rollback=false),SpringData手动添加事务的支持,默认是回滚事务
- 最好都返回List
或Optional - >,不返回T,因为不确保查到的是唯一的
- Specification:查询条件。自定义我们自己的Specification实现类,匿名类或Lambda
- root
- CriterialQuery
- CriterialBuilder
- 模糊匹配,gt, lt, ge, le, like: 得到path对象,根据path指定比较的参数类型,再去比较。path.as(类型的字节码对象),例如path.as(String.class)
