Java for Web学习笔记(一零三):Spring框架中使用JPA(3)JPA仓库
程序员文章站
2022-04-22 13:39:52
...
小例子
我们使用之前JPA小例子的数据库,有三个表Authors,Books和Publishers。先对表Author进行数据读写,读写无非就是增删改查,也就是CRUD。之前,我们已经如何进行Entity和数据库表格的对应,在此略过。
创建仓库接口
public interface AuthorRepository {
Iterable<Author> getAll();
Author get(long id);
void add(Author author);
void update(Author author);
void delete(Author author);
void delete(long id);
}
仓库接口的实现
@Repository
public class DefaultAuthorRepository implements AuthorRepository{
//【注意】上一学习了解到:EntityManager不使用@Inject或者@Autowire,而是使用@PersistenceContext从proxy(SharedEntityManagerBean)中获取的在同一事务内唯一的entityManager对象。
@PersistenceContext EntityManager entityManager;
/* 例子使用的是Java Persistence Query Language (JPQL)。 JPQL不使用表名(已经进行了映射),而是使用类名 */
@Override
public Iterable<Author> getAll() {
return this.entityManager.createQuery("SELECT a FROM Author a ORDER BY a.name",Author.class)
.getResultList();
}
@Override
public Author get(long id) {
return this.entityManager.createQuery("SELECT a FROM Author a WHERE a.id = :id", Author.class)
.setParameter("id", id)
.getSingleResult();
}
@Override
public void add(Author author) {
this.entityManager.persist(author);
}
@Override
public void update(Author author) {
this.entityManager.merge(author);
}
@Override
public void delete(Author author) {
this.entityManager.remove(author);
}
@Override
public void delete(long id) {
this.entityManager.createQuery("DELETE FROM Author a WHERE a.id = :id")
.setParameter("id", id)
.executeUpdate();
}
}
CRUD的通用代码
增删改查是最基本的数据访问,我们要为每个表实现这样的操作,代码是类似的,只是映射到不同的对象,下面介绍通用的代码。
通用接口定义:GenericRepository
I是index的类型,E为Entity的类型。@Validated
public interface GenericRepository<I extends Serializable, E extends Serializable> {
@NotNull Iterable<E> getAll();
E get(@NotNull I id);
void add(@NotNull E entity);
void update(@NotNull E entity);
void delete(@NotNull E entity);
void deleteById(@NotNull I id); //无法区别E和I,因此需采用不同的方法名字
}
相应的,仓库接口可以写为:public interface BookRepository extends GenericRepository<Long, Book>{
Book getByIsbn(@NotNull String isbn); //增加其他接口
}
public interface PublisherRepository extends GenericRepository<Long, Publisher>{ } //无增加
获取类型I和E:GenericBaseRepository
我们需要从BookRepository<Long, Book>中获取正确的Long,Book类型。下面是相关代码:/* 在此给出一个通用的方法,我们并没有具体实现JPA,因为有可能是non-JPA的仓库。对于JPA,进一步使用GenericJPARepository来继承。
* abstract表示不能使用实现这个接口。*/
public abstract class GenericBaseRepository<I extends Serializable, E extends Serializable> implements GenericRepository<I,E>{
protected final Class<I> idClass;
protected final Class<E> entityClass;
@SuppressWarnings({ "unchecked", "rawtypes" })
public GenericBaseRepository(){
/* 目的是通过((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()来获取<I,E>中I和E的具体类型。由于这个GenericBaseRepository被继承后,还可能多次再被继承,因此需要一直向上找到super class中带有具体ParameterizedType的class。代码中我们一直向上找到具体的指明参数类型。很显然当找到GenericBaseRepository就说明失败,无需向上走了。
* 即使我们只使用一次集成,并确保如此,也不能直接使用((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments(),因为对于Spring框架中的transaction proxying和exception translation也存在继承,直接一步获取类型可能会出错,因此需采用追溯的方式。 */
Type genericSuperclass = this.getClass().getGenericSuperclass();
while(!(genericSuperclass instanceof ParameterizedType)){
if(!(genericSuperclass instanceof Class))
throw new IllegalStateException("Unable to determine type arguments " +
"because generic superclass neither parameterized type nor class.");
if(genericSuperclass == GenericBaseRepository.class)
throw new IllegalStateException("Unable to determine type " +
"arguments because no parameterized generic superclass found");
genericSuperclass = ((Class)genericSuperclass).getGenericSuperclass();
}
ParameterizedType type = (ParameterizedType)genericSuperclass;
Type[] arguments = type.getActualTypeArguments();
this.idClass = (Class<I>)arguments[0];
this.entityClass = (Class<E>)arguments[1];
}
}
通用的JPA实现:GenericJPARepository
public abstract class GenericJpaRepository<I extends Serializable, E extends Serializable> extends GenericBaseRepository<I, E>{
@PersistenceContext protected EntityManager entityManager;
//【注意】query.from(this.entityClass)对应到SQL就是表格,由于没有明确类名,不能使用JPQL语句,而是使用 criteria API
@Override
public @NotNull Iterable<E> getAll() {
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<E> query = builder.createQuery(this.entityClass);
return this.entityManager.createQuery(query.select(query.from(this.entityClass)))
.getResultList();
}
@Override
public E get(@NotNull I id) {
return this.entityManager.find(this.entityClass, id);
}
@Override
public void add(@NotNull E entity) {
this.entityManager.persist(entity);
}
@Override
public void update(@NotNull E entity) {
this.entityManager.merge(entity);
}
@Override
public void delete(@NotNull E entity) {
this.entityManager.remove(entity);
}
/* 这里的属性名为id(每个类都如此),如果不是,需要deleteById(@NotNull I id, String idName)。 */
@Override
public void deleteById(@NotNull I id) {
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
CriteriaDelete<E> query = builder.createCriteriaDelete(this.entityClass);
this.entityManager.createQuery(query.where(builder.equal(query.from(this.entityClass).get("id"), id)))
.executeUpdate();
}
}
最后的deleteById相当于
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
CriteriaDelete<E> query = builder.createCriteriaDelete(this.entityClass);
Root<E> root = query.from(this.entityClass); //读取entityClass对应的表
query.where(builder.equal(root.get("id"), id)); //读取的条件,
this.entityManager.createQuery(query).executeUpdate(); //因为query是createCriteriaDelete,因此执行就是delete
我们再看criteria API的例子:要求按name对应列进行升序排列,获取所有的书,代码如下:
...
Root<Book> root = query.from(Book.class);
return this.entityManager.createQuery(query.select(root).orderBy(builder.asc(root.get("name"))))
.getResultList();
具体的仓库实现
有了通用的实现,具体的实现就可以大大简化代码@Repository
public class DefaultBookRepository extends GenericJpaRepository<Long, Book> implements BookRepository{
@Override
public Book getByIsbn(String isbn){
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Book> query = builder.createQuery(this.entityClass);
Root<Book> root = query.from(this.entityClass);
return this.entityManager.createQuery(query.select(root).where(builder.equal(root.get("isbn"), isbn)))
.getSingleResult();
}
}
@Repository
public class DefaultPublisherRepository extends GenericJpaRepository<Long, Publisher> implements PublisherRepository{
}
相关链接:我的Professional Java for Web Applications相关文章
上一篇: 爆笑,夫妻的笑最是逗啊!
下一篇: 巨损的室友,到了歹毒的程度
推荐阅读
-
Java for Web学习笔记(一零一):Spring框架中使用JPA(1)Transaction(上)
-
Java for Web学习笔记(一零七):Spring框架中使用JPA(7)密码和BCrypt
-
Java for Web学习笔记(一零二):Spring框架中使用JPA(2)Transaction(下)
-
Java for Web学习笔记(一零三):Spring框架中使用JPA(3)JPA仓库
-
Java for Web学习笔记(一零六):Spring框架中使用JPA(6)Isolation和C3P0(下)
-
Java for Web学习笔记(一零五):Spring框架中使用JPA(5)Isolation和C3P0(上)