08 Jun 2018
JPA
Java Persistence API
java 对象与数据库表的映射称为 ORM (Object-relational mapping),JPA是ORM的一种方法。开发者可以通过JPA从关系型数据库中映射、存储、更新和获取数据为Java对象,或者反过来。
JPA是一个标准,有几种具体的实现:
- Hibernate
- EclipseLink
- Apache OpenJPA
JPA使得开发者可以直接操作对象,而不用写SQL语句。也将JPA的实现称为 persistence provider。Java对象与数据库表之间的映射是通过 persistence metadata 实现的,它可以是注解,也可以是xml配置文件。
Entity
被@Entity
注解的实体,对应数据库中的一张表。每个实体必须定义一个主键,必须有一个非final的无参构造函数。
默认情况下类名即为表名,也可以不相同,通过注解@Table(name="NEWTABLENAME")
映射。
Fields
JPA可以通过实例变量或者相应的getter、setter访问字段,JPA默认情况下可以访问所有的字段,如果某个字段不想被访问,可以通过注解@Transient
实现。
默认情况下每个字段映射为表的列,可以通过注解@Column(name = "newColumnName")
映射。
Relationship Mapping
JPA运行定义class直接的关系,比如一个class属于另一个class。一般有如下几种关系:
- @OneToOne
- @OneToMany
- @ManyToOne
- @ManyToMany
关系既可以是单向的也可以是双向的,在双向关系中需要通过属性”mappedBy”在另一个class中指明关系归属方,比如@ManyToMany(mappedBy="attributeOfTheOwningClass")
Entity Manager
Entity Manager 提供到数据库的操作。
有两种方式:
-
Container-managed entity manger
通过注解
@PersistenceContext
注入即可,需要spring jpa的支持 -
Application-managed entity manager
它通过
EntityManagerFactory
创建,该工厂类通过持久化单元配置,持久化单元通过META-INF下的persistence.xml
描述。
EntityManagerFactory
是线程安全的单例,EntityManager
是每个线程有自己的实例。
YAZ(Yet Another Zeratul)
Zeratul的新版本,它提供了一个基础的BaseDao。帮我们实现了增删改查,用法如下:
public class UserDao extends BaseDao<User> {
public UserDao() {
super(User.class);
}
@PersistenceContext
public void injectEntityManager(EntityManager entityManager) {
super.setEntityManager(entityManager);
}
public List<User> findUserByName(String key) {
return where(field("name").like(key), field("status").ne(DISABLED)).queryList();
}
}
有时候会添加一个BaseDaoWrapper:
public abstract class BaseDaoWrapper<T> extends BaseDao<T> {
protected BaseDaoWrapper(Class<T> prototype) {
super(prototype);
}
@PersistenceContext
public void injectEntityManager(EntityManager entityManager) {
super.setEntityManager(entityManager);
}
}
public class UserDao extends BaseDaoWrapper<User> {
public UserDao() {
super(User.class);
}
public List<User> findUserByName(String key) {
return where(field("name").like(key), field("status").ne(DISABLED)).queryList();
}
}
如果使用DDD,则会如下使用:
public class UserDao extends BaseDaoWrapper<User> {
public UserDao() {
super(User.class);
}
}
public class UserRepository extends BaseRepository<User> {
private UserDao userDao;
public User find(String userId) {
return userDao.idEquals(userId).querySingle();
}
}
Querydsl
Querydsl 是一个类型安全的 Java 查询框架,支持 JPA, JDO, JDBC, Lucene, Hibernate Search 等标准。类型安全(Type safety)和一致性(Consistency)是它设计的两大准则。在 Spring Boot 中可以很好的弥补 JPA 的不灵活,实现更强大的逻辑。
Querydsl包含的模块有:
- JPA
- JDO
- SQL
- Lucene
- Mongodb
- Collections
引入querydsl插件,但不引入querydsl-jpa,可以使系统不引入jpa,通过SQL的方式实现存储层
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.10"
}
}
repositories {
mavenCentral()
}
apply plugin: 'com.ewerk.gradle.plugins.querydsl'
configurations {
querydsl.extendsFrom compileClasspath
}
dependencies {
compile 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final'
compile("com.querydsl:querydsl-sql:4.2.1")
}
ideaModule.dependsOn compileQuerydsl
querydsl {
library = "com.querydsl:querydsl-apt"
jpa = true
}
project.afterEvaluate {
project.tasks.compileQuerydsl.options.compilerArgs = [
"-proc:only",
"-processor", project.querydsl.processors()
]
}
该插件会查找使用javax.persistence.Entity注解的域类型,并为它们生成对应的查询类型。
使用query-jpa,它包含以下几种用法
-
使用yaz + querydsl + spring jpa
public class BaseRepositoryWrapper<T> extends BaseDao<T> { protected BaseRepositoryWrapper(Class<T> prototype) { super(prototype); } protected boolean isManaged(T entity) { return entityManager.contains(entity); } protected <T> JPAQuery<T> from(EntityPath<T> from) { JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager); return jpaQueryFactory.selectFrom(from); } protected <T> JPAQuery<T> select(Expression<T> what) { JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager); return jpaQueryFactory.select(what); } @PersistenceContext public void injectEntityManager(EntityManager entityManager) { super.setEntityManager(entityManager); } }
-
使用querydsl + spring jpa
public class QuerydslJpaSupport { private EntityManager entityManager; protected <T> JPAQuery<T> from(EntityPath<T> from) { JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager); return jpaQueryFactory.selectFrom(from); } protected <T> JPAQuery<T> select(EntityPath<T> what) { JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager); return jpaQueryFactory.select(what); } protected <T> JPAQuery<T> select(Expression<T> what) { JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager); return jpaQueryFactory.select(what); } protected void store(Object entity) { if (entityManager.contains(entity)) { entityManager.merge(entity); return; } entityManager.persist(entity); } protected JPADeleteClause delete(EntityPath<?> from) { return new JPADeleteClause(entityManager, from); } protected EntityManager entityManager() { return entityManager; } @PersistenceContext protected void setEntityManager(final EntityManager entityManager) { this.entityManager = entityManager; } }
-
要想使用spring data jpa提供的QueryDSL功能,很简单,直接继承接口即可。Spring Data JPA中提供了QueryDslPredicateExecutor接口,用于支持QueryDSL的查询操作接口,如下:
public interface UserRepositoryDls extends JpaRepository<User, Integer>, QueryDslPredicateExecutor<User>{ }
Spring Data Jpa
它是spring提供的一个轻量级ORM持久化框架,它并没有实现JPA,是一个管理工具,JPA的底层通常使用Hibrenate。
我们在实现的时候只需要声明持久化接口,该接口继承Repository或其子接口,Spring会生成实现代码:
public interface UserDao extends JpaRepository<User, Serializable>{
List<User> findByNameLikeAndAgeGreaterThan(String firstName,Integer age);
}
spring 中接口的关系:
- Repository:仅仅只是一个标识,没有任何方法,方便Spring自动扫描识别
- CrudRepository:继承Repository,实现一组CRUD相关方法
- PagingAndStortingRepository:继承CrudRepository,实现一组分页排序相关方法
- JpaRepository:继承PagingAndStortingRepository,实现一组JPA规范方法
Spring 提供了一个便捷的方式使用 Querydsl,只需要在 Repository 中继承 QueryDslPredicateExecutor 即可:
@Repository
public interface UserRepository extends JpaRepository<User, Long>, QueryDslPredicateExecutor<User> {}
然后就可以使用 UserRepository 无缝和 Querydsl 连接:
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
// 基本查询
List<Person> persons = queryFactory.selectFrom(person)
.where(
person.firstName.eq("John"),
person.lastName.eq("Doe"))
.fetch();
// 排序
List<Person> persons = queryFactory.selectFrom(person)
.orderBy(person.lastName.asc(),
person.firstName.desc())
.fetch();
// 子查询
List<Person> persons = queryFactory.selectFrom(person)
.where(person.children.size().eq(
JPAExpressions.select(parent.children.size().max())
.from(parent)))
.fetch();
// 投影
List<Tuple> tuples = queryFactory.select(
person.lastName, person.firstName, person.yearOfBirth)
.from(person)
.fetch()
.map(tuple -> {
Map<String, String> map = new LinkedHashMap<>();
map.put("name", tuple.get(user.name));
map.put("email", tuple.get(user.email));
return map;
}).collect(Collectors.toList());
Spring JPA方法名解析步骤
query方法的命名规则,
- 查询:find…By, read…By, query…By, count…By, and get…By;
- 限制结果数量:findTopBy, findTop1By, findFirstBy, and findFirst1By
- distinct:indTitleDistinctBy or findDistinctTitleBy
Til next time,
at 20:51