CRUD并不足够
在之前学习中,使用了通用仓库接口GenericRepository,并通过GenericBaseRepository,GenericJpaRepository来具体实现,实现上对于CRUD而言,这类的实现大同小异,此外,我们还需要根据UNIQUE KEY或者KEY去进行查询,而不仅仅使用primary key,还有根据多个条件去进行查询或者检索,对返回的结果进行分页等等。GenericRepository提供的基础CRUD是不足够的。
这些接口的实现是比较繁琐的,我们以分页为例。如果我们需要获取能够分多少页,就需要获取总数,代码如下:
@PersistenceContext private EntityManager entityManager;
@Override
public Long count(Predicate ... restrictions) {
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Long> countCriteria = builder.createQuery(Long.class);
Root<TestPage> root = countCriteria.from(TestPage.class);
TypedQuery<Long> countQuery = this.entityManager.createQuery(
countCriteria.select(builder.count(root))
.where(restrictions));
return countQuery.getSingleResult();
}
@Override
public List<TestPage> page(int page, int numPerPage, Predicate ... restrictions) {
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<TestPage> criteria = builder.createQuery(TestPage.class);
Root<TestPage> root = criteria.from(TestPage.class);
TypedQuery<TestPage> pagedQuery = this.entityManager.createQuery(
criteria.select(root)
.orderBy(builder.asc(root.get("userName")))
.where(restrictions));
//【注意】setFristResult:position of the first result, numbered from 0,这个自动分配的id号从1开始不一样
return pagedQuery.setFirstResult((page -1) *numPerPage).setMaxResults(numPerPage).getResultList();
}
我们可以将这两个方法写成通用的代码加入到GenericJpaRepository中,但在获取分页的信息时,可能指定某种或者某些排序,也可以需要进行过滤,也就是在例子中的where(restrictions),这使得调用方陷入到JPA的代码中,如果根据具体场景具体化,可能需要为很多Entity的仓库添加代码,无法做到通用。
Spring Data的作用
仓库的代码不包含任何的业务逻辑(在Service),也不包含UI(在Controller),是可以通用的样板代码。Spring Data它是独立于Spring framework的一个项目。程序员只需定义接口,Spring Data在runtime时可以动态生成所需的仓库代码。
Spring Data支持JPA,JdbcTemplate,NoSQL等,包含所有这些是Spring Data Commons,我们也可以只选择实现JAP的部分,即Spring Data JPA。
简单小例子
Pom的引入
<!-- 在我们的小实验中,spring-data-jpa不能使用2的版本,注入出现异常 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.11.10.RELEASE<version>
</dependency>
如果我们希望使用spring data jpa 2的版本(例如2.0.5.RELEASE),就需要升级spring framework至5.x,而spring framework 5.x要求javax.servlet是4.0的。
在上下文配置Spring data
只需加上@EnableJpaRepositories
@EnableJpaRepositories("cn.wei.flowingflying.chapter22.site.repositories")
/* 如果我们在entityManagerFactory和transactionManager中没有使用默认的方法名,需要明确指出
* @EnableJpaRepositories( basePackages = "cn.wei.flowingflying.chapter22.site.repositories"
* entityManagerFactoryRef = "entityManagerFactoryBean", //缺省为entityManagerFactory
* transactionManagerRef = "jpaTransactionManager" ) //缺省为transactionManager
*/
@ComponentScan(
basePackages = "cn.wei.flowingflying.chapter22.site",
excludeFilters = @ComponentScan.Filter({Controller.class, ControllerAdvice.class})
)
public class RootContextConfiguration implements AsyncConfigurer, SchedulingConfigurer{
... ...
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws PropertyVetoException{
Map<String, Object> properties = new Hashtable<>();
properties.put("javax.persistence.schema-generation.database.action","none");
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
factory.setDataSource(this.springJpaDataSource());
factory.setPackagesToScan("cn.wei.flowingflying.chapter22.site.entity");
factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
factory.setValidationMode(ValidationMode.NONE);
factory.setJpaPropertyMap(properties);
return factory;
}
@Bean
public PlatformTransactionManager transactionManager() throws PropertyVetoException{
return new JpaTransactionManager(this.entityManagerFactory().getObject());
}
}
仓库接口
既然Spring data可以为我们自动生成代码,那么如何写仓库的接口就成为关键。org.springframework.data.repository.Repository<T, ID extends Serializable>是Spring Data中最基础的接口,没有任何方法,其他的接口都是继承它。T就是Entity的类,ID就是主键的类型。
/* CrudRepository是Spring Data提供的接口增删改查的仓库接口,我们只需继承即可,它将提供下面的接口
* ➤ count()返回long,就是total number
* ➤ delete(T), delete(ID), delete(Iterable<? extends T>), deleteAll()
* ➤ exists(ID)查询ID是否存在,返回boolean
* ➤ findAll(),findAll(Iterable<ID>),均返回Iterable<T>
* ➤ findOne(ID)
* ➤ save(S),其中S是T的继承,可能执行insert,也可能执行update操作,返回S(如果是insert,我们可以从中获取自动分配的ID)
* ➤ save(Iterable<S>),返回Iterable<S>,同样S是T的继承
*/
public interface AuthorRepository extends CrudRepository<Author,Long>{
}
如果我们需要排序或者分页,就要继承org.springframework.data.repository.PagingAndSortingRepository<T, ID extendsSerializable>,提供了:
@Service
public class DefaultBookManager implements BookManager {
@Inject AuthorRepository authorRepository;
@Override
@Transactional
public List<Author> getAuthors() {
// return this.toList(authorRepository.getAll()); //以前的代码
return this.toList(authorRepository.findAll());
}
@Override
@Transactional
public void saveAuthor(Author author) {
/* 以前的代码
if(author.getId() < 1)
this.authorRepository.add(author);
else
this.authorRepository.update(author); */
this.authorRepository.save(author);
}
/* 【问题】为何仓库返回的是一个Iterable<E>,我们要转换为Collection,如List?
* 在原生的JDBC镇南关,繁华的是ResultSet,是Iterable,这使得我们可以执行语句,同时继续从数据库中读取信息。
* 在事务方法中,我们已经commit了事务,关闭了ResultSet,转换为list这类collection,确保所有的数据在事务结束前已经被读取。
*/
private <E> List<E> toList(Iterable<E> i){
List<E> list = new ArrayList<>();
i.forEach(list::add);
return list;
}
... ...
}
- findAll(Sort)
- findAll(Pageable)
使用仓库接口
@Service
public class DefaultBookManager implements BookManager {
@Inject AuthorRepository authorRepository;
@Override
@Transactional
public List<Author> getAuthors() {
// return this.toList(authorRepository.getAll()); //以前的代码
return this.toList(authorRepository.findAll());
}
@Override
@Transactional
public void saveAuthor(Author author) {
/* 以前的代码
if(author.getId() < 1)
this.authorRepository.add(author);
else
this.authorRepository.update(author); */
this.authorRepository.save(author);
}
/* 【问题】为何仓库返回的是一个Iterable<E>,我们要转换为Collection,如List?
* 在原生的JDBC镇南关,繁华的是ResultSet,是Iterable,这使得我们可以执行语句,同时继续从数据库中读取信息。
* 在事务方法中,我们已经commit了事务,关闭了ResultSet,转换为list这类collection,确保所有的数据在事务结束前已经被读取。
*/
private <E> List<E> toList(Iterable<E> i){
List<E> list = new ArrayList<>();
i.forEach(list::add);
return list;
}
... ...
}
相关链接: 我的Professional Java for Web Applications相关文章

3126

被折叠的 条评论
为什么被折叠?



