SpringDataJpa学习笔记
前言
官方文档:https://docs.spring.io/spring-data/jpa/docs/2.1.6.RELEASE/reference/html/
maven地址:https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa
pom 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
使用
定义 repository 接口,继承 JpaRepository 接口,指定实体类和主键,添加 @Repository 注解
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}
原理
@JpaRepository
@JpaRepository extends @PagingAndSortingJpaRepository extends CrudRepository extends Repository
@PagingAndSortingJpaRepository:分页和排序查询
@CrudRepository:CRUD
@Repository:作为一个标识
@JpaSpecificationExecutor:条件查询
动态代理
注入对象全限定名:org.springframework.data.jpa.repository.support.SimpleJpaRepository@2c435625
注入对象类型:class com.sun.proxy.$Proxy107(基于JDK动态代理方式创建的对象)
SimpleJpaRepository
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {}
接口介绍
继承 Repository 接口
Repository 接口是 Spring Data Jpa 中为我们提供的所有接口中的顶层接口
Repository 提供了两种查询方式的支持
- 基于方法名称命名规则的查询方式
- 基于 @Query 注解的查询方式
@org.springframework.stereotype.Repository
public interface UserRepositorySelf extends Repository<User, Integer> {
}
基于方法命名规则
规则:findBy + 属性名称(属性名称首字母大写) + 查询条件(首字母大写)
查询条件如下:
Keyword | Sample | JPQL snippet |
---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals | findByFirstname ,findByFirstnameIs ,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull | findByAgeIsNull | … where x.age is null |
IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended % ) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended % ) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in % ) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection<Age> ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection<Age> ages) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1) |
基于 @Query 注解查询
JPQL语句查询
JPQL:通过 Hibernate 的 HQL 演变而来
// 1.通过形参顺序
@Query(value = "from User where username like %?1% and age = ?2")
List<User> queryUserByName(String name, Integer age);
// 2.通过形参名称
@Query(value = "from User where username like %:name% and age = :age")
List<User> queryUsers(String name, Integer age);
原生SQL查询
// 1.通过形参顺序
@Query(value = "select * from t_user where username like ?1 and age = ?2", nativeQuery = true)
List<User> nativeQueryUsers(String username, Integer age);
// 2.通过形参名称
@Query(value = "select * from t_user where username like %:username% and age = :age", nativeQuery = true)
List<User> nativeQueryUsersParam(@Param("username") String username, @Param("age") Integer age);
数据更新
// 1.通过形参顺序,更新时必须在事务中进行
@Query(value = "update User set username = ?1 where id = ?2")
@Modifying //更新
@Transactional
void updateUser(String username, Integer id);
// 2.通过形参名称,更新时必须在事务中进行
@Query(value = "update User set username = :username where id = :id")
@Modifying //更新
@Transactional
void updateUser(@Param("id") Integer id, @Param("username") String username);
继承 CurdRepository 接口
public interface UserCrudRepository extends CrudRepository<User, Integer> {
}
api
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
// 保存或者修改
<S extends T> S save(S entity);
// 保存或者修改集合中的对象
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
// 根据id获取
Optional<T> findById(ID id);
// id 是否存在
boolean existsById(ID id);
// 查询所有
Iterable<T> findAll();
// 通过 id 数组查询
Iterable<T> findAllById(Iterable<ID> ids);
// 统计记录数
long count();
// 根据id删除
void deleteById(ID id);
// 根据实体删除
void delete(T entity);
// 删除指定的所有
void deleteAll(Iterable<? extends T> entities);
// 删除所有
void deleteAll();
}
继承 PagingAnd SortingRepository 接口
public interface UserPagingAndSortingRepository extends PagingAndSortingRepository<User, Integer> {
}
分页
@GetMapping("/page/{size}/{page}")
public Page<User> pageQuery(@PathVariable(name = "size")Integer size, @PathVariable("page")Integer page){
Pageable pageable = PageRequest.of(page, size);
return userPagingAndSortingRepositoryj.findAll(pageable);
}
排序
@PostMapping("/sort")
public List<User> sortQuery(@RequestBody List<String> sort){
Sort.Order order = Sort.Order.asc(sort.get(0));
Sort.Order order1 = Sort.Order.desc(sort.get(1));
Sort by = Sort.by(order, order1);
return (List<User>) userPagingAndSortingRepositoryj.findAll(by);
}
继承 JpaRepository 接口
JapRepository 接口使我们开发时使用最多的接口。其特点可以帮我们把其他接口的方法的返回值做适配处理。开发更高效。
public interface UserRepository extends JpaRepository<User, Integer> {
}
api
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
// 查询所有
List<T> findAll();
// 查询所有,并排序
List<T> findAll(Sort sort);
// 根据 ID 集合获取查询结果
List<T> findAllById(Iterable<ID> ids);
// 修改或者新增
<S extends T> List<S> saveAll(Iterable<S> entities);
//
void flush();
//
<S extends T> S saveAndFlush(S entity);
// 根据集合条件,批量删除
void deleteInBatch(Iterable<T> entities);
// 删除所有
void deleteAllInBatch();
// 根据id获取
T getOne(ID id);
//
<S extends T> List<S> findAll(Example<S> example);
//
<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
继承 JpaSpecificationExecutor 接口
完成多条件查询,并且支持分页与排序。
注:不允许单独使用必须和 JpaRepository 接口一起使用
多条件查询
方式一:
@GetMapping("users")
public List<User> query1(User user){
Specification<User> specification = new Specification<User>() {
/**
* Predicate:定义了查询条件
* @param root :根对象,封装了查询条件的对象
* @param query :定义了基本查询,一般不适用
* @param criteriaBuilder :创建一个查询条件
* @return
*/
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
// and查询条件数组
List<Predicate> ands = new ArrayList<>();
// Predicate 数组
Predicate[] predicatesAnd = new Predicate[ands.size()];
// or查询条件数组
List<Predicate> ors = new ArrayList<>();
// Predicate 数组
Predicate[] predicatesOr = new Predicate[ands.size()];
ands.add(criteriaBuilder.like(root.get("username"), user.getUsername()));
ands.add(criteriaBuilder.equal(root.get("age"), user.getAge()));
ors.add(criteriaBuilder.like(root.get("password"), user.getPassword()));
ors.add(criteriaBuilder.equal(root.get("id"),user.getId()));
Predicate and = criteriaBuilder.and(ands.toArray(predicatesAnd));
Predicate or = criteriaBuilder.or(ors.toArray(predicatesOr));
return criteriaBuilder.or(and,or);
}
};
return userRepository.findAll(specification);
}
执行的SQL
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.password as password3_0_, user0_.username as username4_0_ from t_user user0_
where
(user0_.username like ?) and user0_.age=18 or user0_.password like ? or user0_.id=1
方式二:
@RequestMapping("users2")
public List<User> query2(User user){
Specification<User> specification = new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.or(
criteriaBuilder.and(
criteriaBuilder.like(root.get("username"), user.getUsername()),
criteriaBuilder.like(root.get("password"), user.getPassword())),
criteriaBuilder.or(
criteriaBuilder.equal(root.get("id"), user.getId()),
criteriaBuilder.equal(root.get("age"), user.getAge())
)
);
}
};
return userRepository.findAll(specification);
执行的SQL
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.password as password3_0_, user0_.username as username4_0_ from t_user user0_
where
(user0_.username like ?) and (user0_.password like ?) or user0_.id=1 or user0_.age=18
分页查询
@RequestMapping("3")
public Page<User> query3(User user, int page, int size){
Specification<User> specification = new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.like(root.get("username").as(String.class), user.getUsername());
}
};
Pageable pageable = PageRequest.of(page, size);
return userRepository.findAll(specification, pageable);
}
执行的SQL
总共执行了两条语句
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.password as password3_0_, user0_.username as username4_0_ from t_user user0_ where user0_.username like ? limit ?, ?
Hibernate: select count(user0_.id) as col_0_0_ from t_user user0_ where user0_.username like ?
排序处理
@RequestMapping("4")
public List<User> query4(User user, String sort){
Specification<User> specification = new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.like(root.get("username").as(String.class), user.getUsername());
}
};
Sort s = Sort.by(Sort.Order.desc(sort));
return userRepository.findAll(specification, s);
}
执行的SQL
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.password as password3_0_, user0_.username as username4_0_ from t_user user0_
where user0_.username like ? order by user0_.id desc
分页与排序
将排序对象放入分页对象的构造方法中
@RequestMapping("5")
public Page<User> query5(User user, String sort, int page, int size){
Specification<User> specification = new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.like(root.get("username").as(String.class), user.getUsername());
}
};
Sort s = Sort.by(Sort.Order.desc(sort));
Pageable pageable = PageRequest.of(page,size, s);
return userRepository.findAll(specification, pageable);
}
执行的SQL
Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.password as password3_0_, user0_.username as username4_0_ from t_user user0_ where user0_.username like ? order by user0_.id desc limit ?
Hibernate: select count(user0_.id) as col_0_0_ from t_user user0_ where user0_.username like ?
自定义 Repository 接口
自定义接口
package top.simba1949.dao;
import top.simba1949.common.User;
/**
* @author simba1949@outlook.com
* @date 2019/5/2 20:09
*/
public interface MyRepository {
User myFindById(Integer id);
}
注入的接口继承 MyRepository 接口和 JpaRepository 接口
package top.simba1949.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import top.simba1949.common.User;
/**
* @author simba1949@outlook.com
* @date 2019/5/2 20:11
*/
public interface MyDao extends JpaRepository<User,Integer>, MyRepository {
}
实现 MyRepository 接口,名称必须是 注入接口名称+Impl
package top.simba1949.dao;
import top.simba1949.common.User;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
/**
* @author simba1949@outlook.com
* @date 2019/5/2 20:11
*/
public class MyDaoImpl implements MyRepository {
// 用于操作数据库对象
@PersistenceContext(name = "entityManagerFactory")
private EntityManager em;
@Override
public User myFindById(Integer id) {
User user = em.find(User.class, id);
return user;
}
}
一对一关系
假设 用户与角色一对一关系
用户(user)
package top.simba1949.common;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author simba1949@outlook.com
* @date 2019/5/2 12:49
*/
@Data
@Entity
@Table(name = "t_user")
public class User implements Serializable {
private static final long serialVersionUID = 1534107220170354097L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "username")
private String username;
@Column(name = "password")
private String password;
@Column(name = "age")
private Integer age;
// @OneToOne 表示一对一的关系,cascade = CascadeType.PERSIST表示完成一个级联操作
// @JoinColumn 表示维护一个外键
// @JsonIgnoreProperties(ignoreUnknown = true, value = {"user"}) 防止对象互相嵌套获取
@OneToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "role_id")
@JsonIgnoreProperties(ignoreUnknown = true, value = {"user"})
private Role role;
}
角色(role)
package top.simba1949.common;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.ToString;
import javax.persistence.*;
/**
* @author simba1949@outlook.com
* @date 2019/5/3 14:36
*/
@Data
@Entity
@Table(name = "t_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "role_name")
private String roleName;
// @JsonIgnoreProperties(ignoreUnknown = true, value = {"role"}) 防止对象互相嵌套获取
@ToString.Exclude
@JsonIgnoreProperties(ignoreUnknown = true, value = {"role"})
@OneToOne(mappedBy = "role")
private User user;
}
一对多关系
级联保存只能从维护关系的表或者对象发起,才会生效
假设 一个角色可以给多个用户,一个用户只能拥有一个角色
用户(user)多方
package top.simba1949.common;
import com.fasterxml.jackson.annotation.*;
import lombok.Data;
import lombok.ToString;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author simba1949@outlook.com
* @date 2019/5/3 15:53
*/
@Data
@Entity
@Table(name = "t_user")
public class OneToManyUser implements Serializable {
private static final long serialVersionUID = -601018800979166102L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "username")
private String username;
@Column(name = "password")
private String password;
@Column(name = "age")
private Integer age;
// @JsonIgnoreProperties(ignoreUnknown = true, value = {"users"}) 防止对象嵌套
// @ManyToOne(cascade = CascadeType.ALL) 多对一 ,cascade = CascadeType.ALL 级联操作
// @JoinColumn(name = "role_id") 指定外键
@ToString.Exclude
@JsonIgnoreProperties(ignoreUnknown = true, value = {"users"})
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "role_id")
private OneToManyRole oneToManyRole;
}
角色(role)一方
package top.simba1949.common;
import com.fasterxml.jackson.annotation.*;
import lombok.Data;
import lombok.ToString;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
/**
* @author simba1949@outlook.com
* @date 2019/5/3 15:52
*/
@Data
@Entity
@Table(name = "t_role")
public class OneToManyRole implements Serializable {
private static final long serialVersionUID = 974402828851777226L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "role_name")
private String roleName;
// @JsonIgnoreProperties(ignoreUnknown = true, value = {"role"}) 防止对象嵌套
// @OneToMany(mappedBy = "oneToManyRole", cascade = CascadeType.PERSIST) 一对多
// cascade = CascadeType.PERSIST 级联操作
// 最好使用list集合,set集合会出现问题
@ToString.Exclude
@JsonIgnoreProperties(ignoreUnknown = true, value = {"role"})
@OneToMany(mappedBy = "oneToManyRole", cascade = CascadeType.PERSIST)
private List<OneToManyUser> users;
}
多对多关系
假设用户与角色多对多关系,一个角色拥有多个用户,一个用户拥有多个角色
级联保存只能从维护关系的表或者对象发起,才会生效
用户(user)多方
package top.simba1949.common;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.ToString;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
/**
* @author simba1949@outlook.com
* @date 2019/5/3 17:58
*/
@Data
@Entity
@Table(name = "t_user")
public class ManyToManyUser implements Serializable {
private static final long serialVersionUID = -8624304894035432160L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "username")
private String username;
@Column(name = "password")
private String password;
@Column(name = "age")
private Integer age;
// anyToMany(mappedBy = "users", cascade = CascadeType.ALL) 多对多,
// mappedBy = "users"对应对象的属性,
// cascade = CascadeType.ALL 级联操作
@ToString.Exclude
@JsonIgnoreProperties(ignoreUnknown = true, value = {"users"})
@ManyToMany(mappedBy = "users", cascade = CascadeType.ALL)
private List<ManyToManyRole> roles;
}
角色(role)多方
package top.simba1949.common;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.ToString;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
/**
* @author simba1949@outlook.com
* @date 2019/5/3 17:58
*/
@Data
@Entity
@Table(name = "t_role")
public class ManyToManyRole implements Serializable {
private static final long serialVersionUID = 1375110139795350779L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "role_name")
private String roleName;
// @JoinTable(name = ""),name指定中间表,
// joinColumns = @JoinColumn(name = "role_id") 当前表在中间表的外键名
// inverseJoinColumns = @JoinColumn(name = "user_id")) 表示另外一表在中间表的外键
@ToString.Exclude
@JsonIgnoreProperties(ignoreUnknown = true, value = {"roles"})
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "t_user_role", joinColumns = @JoinColumn(name = "role_id"), inverseJoinColumns = @JoinColumn(name = "user_id"))
private List<ManyToManyUser> users;
}