JPA
JPA (Java Persistence API)Java持久化API。是一套Sun公司Java官方制定的ORM 方案,是规范,是标准 ,sun公司自己并没有实现。
Spring Data JPA
Spring Data JPA是Spring Data家族的一部分,可以轻松实现基于JPA的存数据访问层。该框架基于JPA的方式对数据访问层增强,它使Spring驱动的应用的数据访问层构建更加容易。
pom.xml 1 2 3 4 5 6 7 8 9 10 11 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
application.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://xxx:3306/mall?serverTimezone=GMT%2b8 username: xxxx password: xxxx jpa: hibernate: ddl-auto: update naming: physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl show-sql: true
数据库基本配置
自动更新数据库表
使用驼峰命名方式
显示sql
Application.java 1 2 3 4 5 @EnableJpaRepositories @SpringBootApplication public class Application { ... }
@EnableJpaRepositories取代xml形式的配置,使用注解方式
Entity BaseEntity.java(Auditing)
Spring Data JPA提供Entity监听器,让你可以在对实体进行操作时记录信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Getter @Setter @DynamicUpdate @DynamicInsert @MappedSuperclass @EntityListeners(AuditingEntityListener.class) public abstract class BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) protected Long id; @CreatedDate @Column(updatable = false) private Long createTime; @LastModifiedDate @Column private Long updateTime; @CreatedBy @Column(name = "created_by", updatable = false, length = 64) private String createdBy; @LastModifiedBy @Column(name = "updated_by", length = 64) private String updatedBy; @Column(columnDefinition = "tinyint(1) default 0") private Boolean deleted=false; }
@MappedSuperclass该类作为父类时使用此注解则不会更新该实体到数据库
@DynamicInsert @DynamicUpdate生成sql时会忽略值为null的字段
@EntityListeners(AuditingEntityListener.class),使用审计的实体监听器,配合@CreatedDate @LastModifiedDate @CreatedBy @LastModifiedBy (依赖spring-aspects.jar)
更多Base类请前往SpringDataJPA之Base抽象类-范型快速实现增删查改
Application.java 1 2 3 4 5 6 7 8 9 10 @EnableJpaRepositories @EnableJpaAuditing @SpringBootApplication public class MallApplication { public static void main(String[] args) { SpringApplication.run(MallApplication.class, args); } }
AuditorAwareImpl.java 1 2 3 4 5 6 7 8 @Component public class AuditorAwareImpl implements AuditorAware<String> { @Override public Optional<String> getCurrentAuditor() { return Optional.of(SecurityContextHolder.getContext().getAuthentication().getName()); } }
实现AuditorAware重写getCurrentAuditor控制@CreatedBy和@LastModifiedBy的值
SecurityContextHolder.getContext().getAuthentication().getName()从Security上下文获取当前请求用户的name
多对多Entity
不建议使用@ManyToMany,否则关联表无法添加冗余字段
建议建立关联表,实体表属性利用@OneToMany对应关联表属性@ManyToOne
User.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Getter @Setter @Entity @Table(name = "user") @Where(clause = "deleted<>1") public class User extends BaseEntity { ... @JsonManagedReference @JsonIgnore @OneToMany(mappedBy = "user", cascade = {CascadeType.PERSIST}) private Set<UserRole> userRoles = new HashSet<>(); }
Role.java 1 2 3 4 5 6 7 8 9 10 11 12 13 @Getter @Setter @Entity @Table(name = "role") @Where(clause = "deleted<>1") public class Role extends BaseEntity { ... @JsonManagedReference @OneToMany(mappedBy = "role") private Set<UserRole> userRoles = new HashSet<>(); }
UserRole.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Getter @Setter @Entity @Table(name = "user_role") @Where(clause = "deleted<>1") public class UserRole extends RelationBaseEntity { @JsonBackReference @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; @JsonBackReference @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "role_id") private Role role; }
@Entity标记该类为实体类,会与数据库做映射
添加@Where可以使其查询时带指定条件
@JsonManagedReference和@JsonBackReference防止双向引用、循环依赖
@JoingColumn使属性映射到数据库指定字段
Repository UserRepository.java 1 2 public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> { }
JpaReoisitory<T,ID>包含对数据访问层的基本扩展(CRUD方法)
JpaSpecificationExecutor<T>通过构造Specification来定义查询条件
RoleRepository、PermissionRepository同上 基本用法 JpaRepository自带简单的增删查改 1 2 3 4 5 6 7 8 java.util.List<T> findAll(); java.util.List<T> findAll(org.springframework.data.domain.Sort sort); java.util.List<T> findAllById(java.lang.Iterable<ID> iterable); <S extends T> java.util.List<S> saveAll(java.lang.Iterable<S> iterable); ...
重写Specification的toPredicate实现自定义条件查询 单条件 1 2 3 4 5 6 7 Specification<User> specification = new Specification<User>() { @Override public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { return criteriaBuilder.equal(root.get("username"), username); } }; Optional<User> user = userRepository.findOne(specification);
多条件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Specification<User> specification = new Specification<User>() { @Override public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { List<Predicate> predicateList = new ArrayList<>(); if (!name.isEmpty()) { predicateList.add(criteriaBuilder.like(root.get("name"), "%" + name + "%")); } if (roleId != null) { predicateList.add(criteriaBuilder.equal(root.get("roleId"), roleId)); } return criteriaQuery.where(predicateList.toArray(new Predicate[predicateList.size()])).getRestriction(); } }; Optional<User> user = userRepository.findAll(specification);
分页查询 1 Optional<User> user = userRepository.findAll(specification, PageRequest.of(pageNumber - 1, pageSize));
随机查询 1 2 3 4 Expression<Double> e = new BasicFunctionExpression<>( (CriteriaBuilderImpl) criteriaBuilder, Double.class, "rand" ); criteriaQuery.orderBy(criteriaBuilder.desc(e));
触发懒加载 User userData = entityManagerUtil.lazyLoad(User.class,user.getId(), "userRoles","role","rolePermissions");
EntityMangerUtil 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Component public class EntityManagerUtil { @PersistenceContext private EntityManager em; public <T> T lazyLoad(Class<T> t,Long id ,String...subgraphList) { EntityGraph graph = this.em.createEntityGraph(t); Subgraph subGraph = graph.addSubgraph(subgraphList[0]); for (int i = 1; i < subgraphList.length; i++) { subGraph = addSubgraph(subGraph, subgraphList[i]); } Map<String, Object> props = new HashMap<>(); props.put("javax.persistence.fetchgraph", graph); return em.find(t, id, props); } public Subgraph addSubgraph(Subgraph graph, String subgraph) { return graph.addSubgraph(subgraph); } }
lazyLoad方法利用泛型接收T类型的参数返回T类型的数据。 该方法创建对应类型的EntityGraph,递归加入指定的子属性(subgraph),查询指定id的对象返回。
@PersistenceContext 注入容器托管的对象
EntityManager 管理了标注@Entity注解的对象
javax.persistence.fetchgraph 只查询需要的属性
javax.persistence.loadgraph 额外查询需要的属性
Querydsl pom.xml 1 2 3 4 5 6 7 8 9 <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-jpa</artifactId> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <scope>provided</scope> </dependency>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <build> <plugins> <plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin> </plugins> </build>
配置plugin后可以自动生成Q开头的querysdl需要的查询对象
JpaQueryConfig.java 1 2 3 4 5 6 7 @Configuration public class JpaQueryConfig { @Bean public JPAQueryFactory jpaQuery(EntityManager entityManager) { return new JPAQueryFactory(entityManager); } }
多表关联查询 1 2 @Autowired JPAQueryFactory queryFactory;
1 2 3 4 5 6 7 8 9 10 11 QPermission qPermission = QPermission.permission; QRolePermission qRolePermission = QRolePermission.rolePermission; QUser qUser = QUser.user; QUserRole qUserRole = QUserRole.userRole; List<Permission> permissionList = queryFactory.select(qPermission).from(qUser) .leftJoin(qUserRole).on(qUser.id.eq(qUserRole.user.id)) .leftJoin(qRolePermission).on(qRolePermission.role.id.eq(qUserRole.role.id)) .leftJoin(qPermission).on(qPermission.id.eq(qRolePermission.permission.id)) .where(qUser.id.eq(user.getId())) .fetch(); return new HashSet<>(permissionList);