1.开发环境的搭建
导入项目依赖的jar包:
4.0.0
com.imooc
springdata
1.0-SNAPSHOT
mysql
mysql-connector-java
5.1.38
junit
junit
4.10
org.springframework
spring-jdbc
4.3.5.RELEASE
org.springframework
spring-context
4.3.5.RELEASE
org.springframework.data
spring-data-jpa
1.8.0.RELEASE
org.hibernate
hibernate-entitymanager
4.3.6.Final
配置数据源,EntityMangerFactory,事物管理,注解相关等
org.hibernate.cfg.ImprovedNamingStrategy
org.hibernate.dialect.MySQL5InnoDBDialect
true
true
update
2.Spring-data-jpa中的实体配置及其映射关系
@Entity //实体,必填
@Table(name = "t_user") //实体-表映射,如果没有,则自动生成的表名为类名的小写
public class User {
@GeneratedValue //主键策略
@Id //主键ID
private Integer id;
private String name;
@Column(name = "pwd",length=20) //属性和表列的映射,length表示映射到表中列的长度
private String password;
private Integer age;
private Date birthday;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Temporal(TemporalType.DATE) //时间日期格式,详细见下
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
2.1.关于日期格式化的@Temporal注解的使用
@Temporal标签的作用很简单:
1、如果在某类中有Date类型的属性,数据库中存储可能是'yyyy-MM-dd hh:MM:ss'要在查询时获得年月日,在该属性上标注
@Temporal(TemporalType.DATE) 会得到形如'yyyy-MM-dd' 格式的日期。
DATE :等于java.sql.Date
日期:
@Temporal(TemporalType.DATE)
@Column(name = "applyDate", nullable = false, length = 12)
public Date getApplyDate() {
return applyDate;
}
在页面端取值:2016--09--28
2、如果在某类中有Date类型的属性,数据库中存储可能是'yyyy-MM-dd hh:MM:ss'要获得时分秒,在该属性上标注
@Temporal(TemporalType.TIME) 会得到形如'HH:MM:SS' 格式的日期。
TIME :等于java.sql.Time
时间:
@Temporal(TemporalType.TIME)
在页面端取值:15:50:30
3、如果在某类中有Date类型的属性,数据库中存储可能是'yyyy-MM-dd hh:MM:ss'要获得'是'yyyy-MM-dd hh:MM:ss',在该属性上标注
@Temporal(TemporalType.TIMESTAMP) 会得到形如'HH:MM:SS' 格式的日期
TIMESTAMP :等于java.sql.Timestamp
日期和时间(默认):
@Temporal(TemporalType.TIMESTAMP)
在页面端取值:2016-09-28 15:52:32:000
3.Dao的规范
在传统的DAO层中,我们访问数据库,有以下方式jbUtils,jdbcTemplate,Hibernate,Mybatis等框架,而在使用jpa的时候,我们的dao层只需要定义接口,而不需要去定义接口的实现类,spring-data-jpa会自动的帮我们做出配置和实现
在spring-data-jpa中,定义dao层继承去实现Respository接口,即可自动实现一些方法
3.1 Repository接口的层级关系
3.2 在dao接口中继承Repository接口,通过方法名称完成查询
public interface UserRepository extends Repository {
//实体类 实体类主键类型
User findByName(String name);
}
在dao接口中定义方法需要符合jpa的方法定义的规则
a.方法名字以read,find,get开头
b.涉及条件查询的属性要以关键字连接,条件属性首字母大写
c.支持级联查询,后面详述
3.3 通过@Query注解完成查询
/**
* @Query 通过查询实体
* 它的注解传递参数的2种方式
* 1.使用占位符, 此方式要求形参与定义的 JPQL 的参数位置一致,?1表示第一个参数,?2表示第二个参数
* 2.使用使用命名参数, 此方式形参与定义的 JPQL 的参数位置不必一致,:name 表示参数,通过@param 注解来定义该参数
*/
@Query("select o from User o where name = ?1")
User queryUser(String name);
@Query("select o from User o where name = :name")
User queryUser2(@Param("name") String name);
/**
* @Query 通过本地查询,直接查询表,需要配置nativeQuery = true,实现直接查询表,而不是在通过实体查询
*/
@Query(nativeQuery = true ,value = "select count(*) from t_user" )
User queryUserCount();
/**
* 带有like关键字的查询,spring-data允许直接我们在传递参数的时候带%,也可以在占位符参数或者命名参数中使用%
*/
@Query("select o from User o where name like :name")
User queryUserNameLike(@Param("name") String name);//调用时候 queryUserNameLike("%pei%");
@Query("select o from User o where name like %:name%")
User queryUserNameLike1(@Param("name") String name);
@Query("select o from User o where name like %?1%")
User queryUserNameLike2(@Param("name") String name);
3.5. @Query中的删除和更新
通过使用@Modifying注解配合@Query注解完成UPDATE和DELETE,在JPQL中不支持INSERT
UPDATE和DELETE操作需要使用事务,所以我们在调用的service层中添加事务
dao层更新的中方法:
@Modifying
@Query("update User o set o.name = :name where o.id = :id")
User updateUserName(@Param("name") String name,@Param("id")Integer id);
service层中添加事务
@Service
public class UserServiceImpl {
@Autowired
private UserRepository userRepository;
@Transactional //import javax.transaction.Transactional;
public void updateUserName(String name,Integer id) {
userRepository.updateUserName(name, id);
}
}
4. Repository的子接口
4.1 CrudRepository接口,有CRUD相关方法如下,在Service层中调用时候需要添加事务@Transactional
public interface CrudRepository extends Repository {
S save(S entity);
Iterable saveAll(Iterable entities);
Optional findById(ID id);
boolean existsById(ID id);
Iterable findAll();
Iterable findAllById(Iterable ids);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAll(Iterable extends T> entities);
void deleteAll();
}
4.2 PagingAndSortRepository 继承自CrudRepository,除了CRUD的方法外,还有分页和排序的方法
public interface PagingAndSortingRepository extends CrudRepository {
Iterable findAll(Sort sort); //带排序的查询
Page findAll(Pageable pageable); //带排序的分页查询
}
4.2.1 创建排序Sort对象和分页Pageable对象
pageRequest对象的常用构造方法
public PageRequest(int page, int size) {
this(page, size, Sort.unsorted());
}
public PageRequest(int page, int size, Direction direction, String... properties) {
this(page, size, Sort.by(direction, properties));
}
public PageRequest(int page, int size, Sort sort) {
super(page, size);
this.sort = sort;
}
构造Sort对象时候需要传入Order对象,Order对象需要根据列和排序的规则进行创建
在Service中定义方法如下:
public void queryPage() {
//排序的对象
Order order = Order.asc("name");//根据name升序,相当于:Order o = new Order(Direction.ASC, "name");
Sort sort = new Sort(order); //创建排序对象
//分页的对象
//Pageable page = new PageRequest(0, 20);//第一个参数表示:第几页(从0开始,0表示第一页),第二个参数表示:每页的记录数
PageRequest page = new PageRequest(0, 20, sort);
Page pageList = user2Repository.findAll(page);
int totalPages = pageList.getTotalPages();//查询的总页数
long totalCount = pageList.getTotalElements(); //查询的总记录数
int number = pageList.getNumber()+1; //查询的当前第几页,从0开始
List content = pageList.getContent();//当前页面的集合
int ofElements = pageList.getNumberOfElements(); //当前页面的记录数
}
4.3 jpaRepositopry 继承PagingAndSortingRepository,常用方法有:
public interface JpaRepository extends PagingAndSortingRepository {
List findAll();
List findAll(Sort sort);
List findAll(Iterable ids);
List save(Iterable entities);
void flush();
S saveAndFlush(S entity);
void deleteInBatch(Iterable entities);
void deleteAllInBatch();
T getOne(ID id);
}
jpaRepository中saveAndFlush(S entity)相当于JPA的merge()方法,merge()方法如下
① 传入的是一个临时对象(没有 id):会创建一个新的对象,把临时对象的属性复制到新的对象中,然后对新的对象执行持久化操作将新的对象插入了数据库
② 传入的是一个游离对象(有 ID):若在 EntityManager 缓存中没有该对象,在数据库中也没有对应的记录,JPA 会创建一个新的对象,把当前游离对象的属性复制到新的对象中,
对新创建的对象执行 insert 操作,插入的是新的对象
③ 传入的是游离对象,即传入的对象有 OID,缓存中没有,但数据库中有对应的对象:JPA 会查询对应的记录,然后返回该记录对应的对象把当前游离对象的属性复制到查询到的对象中,对查询到的对象执行 update 操作
④ 传入的是游离对象,即传入的对象有 OID,EntityManager 缓存中有对应的对象:JPA 会把当前游离对象的属性复制到查询到的 EntityManager 缓存中的对象,对 EntityManager 缓存中的对象执行 update 操作
5.JpaSpecificationExecutor 接口
Specification中封装了JPA Criteria 的查询条件,之前Repository接口中无法传入查询条件,也就是无法创建条件查询,所以我们需要依赖这个接口去创建条件查询,JpaSpecificationExecutor提供了以下方法,其中Specification就是我们需要传递的条件查询的对象,里面封装了一些我们需要的条件
public interface JpaSpecificationExecutor {
T findOne(Specification spec);
List findAll(Specification spec);
Page findAll(Specification spec, Pageable pageable);
List findAll(Specification spec, Sort sort);
long count(Specification spec);
}
使用的时候多继承一个JpaSpecificationExecutor接口,就可以使用上面的方法了:
public interface User3Repository extends PagingAndSortingRepository ,JpaSpecificationExecutor{
}
那么我们如何创建Specification来创建条件查询呢?
/**
* Root :就是我们需要查询的类型(对象)
* query :添加查询条件
* cb:构建Predicate 查询条件对象
*/
Specification spec = new Specification() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb) {
// Predicate 就是查询条件对象
Path path = root.get("age"); //Root相当于User的根对象,可以通过这个对象获取User的属性,Path 就是相当于是从Root获取age的路径的对象
//CriteriaBuilder 构建对象,可以通过表达式构建条件,cb.gt(path, 30);
return cb.gt(path, 20);
}
};
Page all = user2Repository.findAll(spec, page);
其中:
6. spring-data-jpa的级联查询
提供接口