Spring Data学习笔记
最近看了这个教程学习SpringData技术。老师开头演示了传统的JDBC使用方法,非常的繁琐,需要在逻辑代码中进行配置。每一次操作数据库都要通过建立连接、执行查询、释放资源三部重复的代码,还要写一个JDBCUtil工具类来整合数据库连接和关闭的逻辑:
1 | /** |
为了避免这样繁琐的配置操作,Spring框架内置的 JdbcTemplate模板来了,它将数据的配置独立到了.xml
文件中,和逻辑代码独立开来,很大程度简化了代码,助力开发:
- 添加Maven依赖(搜索
Maven Repository
,在Maven仓库中找到需要的依赖)
这里需要添加两个依赖:spring-jdbc
&spring-context
? - 配置
beans.xml
:DataSource&JdbcTemplate - 开发spring jdbc版本的query和save方法
- 写Test Case
在Spring中,我们将beans.xml
配置文件放在Resources
文件夹下,这里截取部分主要内容:
1 |
|
上面是视频中的代码,由于各种更新,我在《精通Spring 4.x》中学习的下面的代码也是可行的,原理逻辑是一样的,自己对比:
1 | <!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 --> |
总结弊端
- DAO has many many code
- DAOImpl has many duplicate code
- Develop the page and other functions对于开发分页和开发其他方法工作量很大
Spring Data JPA
PS:这部分的总结参考这里
SpringData JPA只是SpringData中的一个子模块
JPA是一套标准接口,而Hibernate是JPA的实现
SpringData JPA 底层默认实现是使用Hibernate
(Hibernate是什么呢?Hibernate是一种Java语言下的对象关系映射(ORM)解决方案。是一种ORM框架,全称为 Object_Relative DateBase-Mapping,在Java对象与关系数据库之间建立某种映射,以实现直接存取Java对象!通俗点说,就是Hibernate能将一个领域对象绑定到数据库的一张表上,无论你是想新建表还是CRUD这张表的数据,你只需要对这个类进行操作就可以了,而不需要直接去操作数据库,教程中的实例就是雇员: 先开发实体类===>自动生成对应的数据表)
开发环境搭建
首先我们要写配置文件,也就是POM和resources
里面的.xml
文件。这部分我踩了个坑,我根据教程写好POM配置以后,并没有办法测试SpringDataTest
。报错信息第一句是:
1 | java.sql.SQLException: Unknown system variable 'query_cache_size' |
感谢isxuran给我提供的解决方案,原因是mysql-connecter-java的版本过低,很显然是数据库驱动程序与数据库版本不对应。所以我修改了mysql-connector-java
的依赖版本,由教程中的5.1.38
改成了5.1.6
。为什么选择5.1.6
呢,我只是在maven repository
里网上找了一个使用率较高的进行尝试,没有报错,问题解决。
1 |
|
beans.xml
中主要配置了数据源dataSource
、Hibernate会用到的EntityManagerFactory
。
1 |
|
Hibernate格式化与不格式化对比图(绿色框内的是格式化后的输出):
接着我们只需要写好domain object和测试类,进行单元测试即可:
单元测试通过后我们会发现数据库中已经新建了一个employee
表:
然后我们再修改一下beans.xml
中的事务管理配置、实现repository
接口即可。
Spring Data JPA进阶
Repository类的定义
1
2public interface Repository<T, ID extends Serializable> {
}1
2
3
4
5
6
7import com.ting.domain.Employee;
import org.springframework.data.repository.Repository;
public interface EmployeeRepository extends Repository<Employee,Integer> {
public Employee findByName(String name);
}1)
Repository
是一个空接口,这种接口称为标记接口
没有包含方法声明的接口
2) 如果我们定义的接口EmployeeRepository
继承了核心的接口Repository
,那么就是告诉了Spring来管理我们的接口。假如我们没有继承的会,运行就会报错:1
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.ting.repository.EmployeeRepository' available
这说明你没有继承核心接口,那么Spring就找不到你定义的接口。
另外我们也可以用注释来代替继承语句,作用是一样的:1
2
3
4
5
6
7
8import com.ting.domain.Employee;
import org.springframework.data.repository.RepositoryDefinition;
.class,idClass = Integer.class) (domainClass = Employee
public interface EmployeeRepository {
public Employee findByName(String name);
}Repository子类接口:CrudRepository,JpaRepository,PagingAndSortingRepository,JpaSpecificationExecutor
在IDEA中crtl+t/h点击相应的类查看继承关系
- CrudRepository:继承Repository,实现了CRUD相关操作
- PagingAndSortingRepository:继承CrudRepository,实现了分页排序的相关方法
- JpaRepository:继承PagingAndSortingRepository,实现JPA规范相关的方法
- 使用Repository接口查询方法定义规则和使用
1 | // where name like ?% and age <? |
弊端:
1)方法名会比较长: 约定大于配置
2)对于一些复杂的查询,是很难实现
因此,对于这种情况下还是要写SQL语句简单得多。所以又引入了@Query
注解
1 | "select o from Employee o where id=(select max(id) from Employee t1)") ( |
对于修改数据,需要增加Modify注解、并且一定要在事务的管理下才能修改数据,读取不需要事务
1 |
|
补充知识:【JPQL】–JPQL和SQL的比较
然后在service层:
1 | package com.ting.services; |
事务在Spring data中的使用:
1)事务一般是在Service层
2)@Query、 @Modifying、@Transactional的综合使用
使用CrudRepository接口:
这个接口继承源代码所提供的方法:1
2
3
4
5
6
7
8
9
10
11<S extends T> S save(S entity);
<S extends T> Iterable<S> save(Iterable<S> entities);
T findOne(ID id);
boolean exists(ID id);
Iterable<T> findAll();
Iterable<T> findAll(Iterable<ID> ids);
long count();
void delete(ID id);
void delete(T entity);
void delete(Iterable<? extends T> entities);
void deleteAll();使用这个接口我们可以实现对数据库的批量操作CRUD
使用分页PagingAndSortingRepository接口:
EmployeePagingAndSortingRepositoryTest.java:
1 | package com.ting.services; |
使用JpaRepository接口
接口源代码所提供的方法:1
2
3
4
5
6
7
8
9List<T> findAll();
List<T> findAll(Sort sort);
List<T> findAll(Iterable<ID> ids);
<S extends T> List<S> save(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
void deleteInBatch(Iterable<T> entities);
void deleteAllInBatch();
T getOne(ID id);补充一个JpaSpecificationExecutor接口
使用时同时extends JpaRepository<Employee,Integer>,JpaSpecificationExecutor<Employee>
Specification封装了JPA Criteria查询条件,可以操作分页、排序、条件查询.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
public void testQuery() {
Sort.Order order = new Sort.Order(Sort.Direction.ASC,"id");
Sort sort = new Sort(order);
Pageable pageable = new PageRequest(1,5,sort);
/**
* Root:我们要查询的类型(Employee)
* query:添加查询条件
* cb:构建Predicate
*/
Specification<Employee> specification = new Specification<Employee>() {
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//root(employee(age))
Path path = root.get("age");
return cb.gt(path,50); //gt:greater than
}
};
Page<Employee> page = employeeJpaSpecificationRepository.findAll(specification,pageable);
System.out.println("查询的总页数" + page.getTotalPages());
System.out.println("查询的总记录数" + page.getTotalElements());
System.out.println("当前第几页" + (page.getNumber()+1));
System.out.println("当前页面的集合" + page.getContent().toString());
System.out.println("当前页面的记录数" + page.getNumberOfElements());
}