SpringDataJPA整合Querydsl & JPA实体类使用Auditing


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);

← Prev RedisTemplate操作Redis | Docker-Compose部署生产环境 Next →