- Spring JPA集成
- 基础查询实例
- 复杂查询+分页
- 一对多查询及多对多查询
1. Spring JPA集成
1.1 项目配置
如截图所示,需要导入对应的组件,其他的和正常创建SpringBoot一致。
1.2 配置文件配置
修改application.yml文件进行如下配置:
spring:
devtools:
restart:
enabled: false
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://[IP地址]:3306/test
hikari:
username:[MYSQL 用户名]
password: [MYSQL 密码]
jpa:
hibernate:
#可选参数
#create 启动时删数据库中的表,然后创建,退出时不删除数据表
#create-drop 启动时删数据库中的表,然后创建,退出时删除数据表 如果表不存在报错
#update 如果启动时表格式不一致则更新表,原有数据保留
#validate 项目启动表结构进行校验 如果不一致则报错
ddl-auto: update
show-sql: true
properties:
hibernate:
enable_lazy_load_no_trans: true
2. 基础查询实例
2.1 类创建
2.1.1 实体类创建
@Data
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
}
对应的注解意思如下:
- @Data:lombok注解,生成对应的getter(),setter(),有参构造方法,无参构造方法等。
- @Entity:表明该类是jpa的一个实体
- @Table:映射到具体的数据库中的表
- @Id: 主键
- @GeneratedValue:生成该值的策略
IDENTITY: 采用数据库ID自增长的方式来自增主键字段,Oracle 不支持这种方式;
AUTO: JPA自动选择合适的策略,是默认选项;
SEQUENCE: 通过序列产生主键,通过@SequenceGenerator 注解指定序列名,MySql不支持这种方式
TABLE: 通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。
- @Column: 显示建立该表的字段连接关系,默认情况会将驼峰命名转成xx_xxx进行映射。
2.1.2 Dao实体创建
public interface UserDao extends JpaRepository<User,Long>, JpaSpecificationExecutor<User> {
@Query(value="select * from user where name like ?1",nativeQuery = true)
public List<User> findSqlByLikeName(String name);
public User findByName(String name);
}
对应继承的类作用:
- JpaRepository<实体类型,主键类型>:封装了基础的CURD操作。
- JpaSpecificationExecutor<操作的实体类类型>:封装了复杂的查询(分页)
对应的操作类型:
- SQL 语句查询:通过@Query注解, nativeQuery : false(使用jpql查询) | true(使用本地查询 :sql查询),第一个参数则为?1,第二个参数则为?2以此类推。
- 方法命名规则查询:
/**
* 方法名的约定:
* findBy : 查询
* 对象中的属性名(首字母大写) : 查询的条件
* CustName
* * 默认情况 : 使用 等于的方式查询
* 特殊的查询方式
*
* findByCustName -- 根据客户名称查询
*
* 再springdataJpa的运行阶段
* 会根据方法名称进行解析 findBy from xxx(实体类)
* 属性名称 where custName =
*
* 1.findBy + 属性名称 (根据属性名称进行完成匹配的查询=)
* 2.findBy + 属性名称 + “查询方式(Like | isnull)”
* findByCustNameLike
* 3.多条件查询
* findBy + 属性名 + “查询方式” + “多条件的连接符(and|or)” + 属性名 + “查询方式”
*/
2.2 测试
2.2.1 简单测试保存
@Autowired
private UserDao userDao;
@Test
void simpleSave(){
User user = new User();
user.setName("测试2");
userDao.save(user);
}
2.2.2 简单测试查询
@Test
void sqlSearch(){
List<User> users = userDao.findSqlByLikeName("测试2");
System.out.println(users);
}
@Test
void matchSearch(){
User user = userDao.findByName("测试");
System.out.println(user);
}
3. 复杂查询+分页
有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。
- JpaSpecificationExecutor方法列表
1. T findOne(Specification spec); //查询单个对象
2. List findAll(Specification spec); //查询列表
3. 查询全部,分页
pageable:分页参数
返回值:分页pageBean(page:是springdatajpa提供的)
Page findAll(Specification spec, Pageable pageable);
4.查询列表
Sort:排序参数
List findAll(Specification spec, Sort sort);
5. long count(Specification spec);//统计查询
我们需要自己实现Specification:
//root:查询的根对象(查询的任何属性都可以从根对象中获取)
//CriteriaQuery:顶层查询对象,自定义查询方式(了解:一般不用)
//CriteriaBuilder:查询的构造器,封装了很多的查询条件
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb); //封装查询条件
测试
3.1 简单的例子
@Test
void testSpec(){
Specification<User> spec = new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//1.获取比较的属性
Path<String> name = root.get("name");
Path<Long> id = root.get("id");
//2.构造查询条件 : select * from user where name = '测试1' and id = 1
/**
* 第一个参数:需要比较的属性(path对象)
* 第二个参数:当前需要比较的取值
*/
Predicate predicate1 = cb.equal(name, "测试2");//进行精准的匹配 (比较的属性,比较的属性的取值)
Predicate predicate2 = cb.equal(id,1);
return cb.and(predicate1,predicate2);
}
};
User user = userDao.findOne(spec).get();
System.out.println(user);
}
3.2 分页的例子
通过Sort进行排序,PageRequest 进行分页,第一个参数为当前查询的页码(从0开始),第二个参数为每页查询的数量,第三个参数为排序字段。
findAll()支持Specification查询和分页,这样就可以实现复杂类型的查询。
@Test
void testPage(){
Specification spe = null;
Sort sort = Sort.by(Sort.Direction.DESC,"id");
PageRequest pageRequest = PageRequest.of(0,2,sort);
Page<User> users = userDao.findAll(spe,pageRequest);
System.out.println(users.getTotalPages());
System.out.println(users.getContent());
}
4. 一对多查询及多对多查询
假设:用户和班级的关系为:1:N,一个用户可以在不同的班级内。
假设:用户和角色的关系为:N:M,多对多的关系在中间表维护
4.1 类改造及新增
4.1.1 User类改造
@Data
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@OneToMany(mappedBy = "user",cascade = CascadeType.ALL)
@org.hibernate.annotations.ForeignKey(name = "none")
private List<ClassE> classE = new ArrayList<>();
@ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
@JoinTable(name = "sys_user_role",
//joinColumns,当前对象在中间表中的外键
joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "id")},
//inverseJoinColumns,对方对象在中间表的外键
inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "id")}
)
private List<Role> roles = new ArrayList<>();
}
4.1.2 ClassE类创建
@Entity
@Table(name = "class")
@Getter
@Setter
public class ClassE {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
/**
* 配置联系人到客户的多对一关系
* 使用注解的形式配置多对一关系
* 1.配置表关系
* @ManyToOne : 配置多对一关系
* targetEntity:对方的实体类字节码
* 2.配置外键(中间表)
*
* * 配置外键的过程,配置到了多的一方,就会在多的一方维护外键
*
*/
@ManyToOne(targetEntity = User.class,fetch = FetchType.EAGER)
@JoinColumn(name = "uid",referencedColumnName = "id",foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
private User user;
}
4.1.3 Role类创建
@Entity
@Getter
@Setter
@Table(name = "role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
//配置多对多
@ManyToMany(mappedBy = "roles") //配置多表关系(对方配置映射关系的属性)
private List<User> users = new ArrayList<>();
}
4.1.4 Dao分别创建
public interface UserDao extends JpaRepository<User,Long>, JpaSpecificationExecutor<User> {
}
public interface RoleDao extends JpaRepository<Role,Long>, JpaSpecificationExecutor<Role> {
}
public interface ClassDao extends JpaRepository<ClassE,Long>, JpaSpecificationExecutor<ClassE> {
}
4.2 测试
@Test
@Transactional
@Rollback(false)
void testRoleAdd(){
User user = new User();
user.setName("测试人员");
ClassE classE = new ClassE();
classE.setName("一班");
ClassE classE1 = new ClassE();
classE.setName("二班");
user.getClassE().add(classE);
user.getClassE().add(classE1);
classE.setUser(user);
classE1.setUser(user);
Role role = new Role();
role.setName("角色1");
Role role1 = new Role();
role1.setName("角色2");
user.getRoles().add(role);
user.getRoles().add(role1);
userDao.save(user);
classDao.save(classE);
classDao.save(classE1);
roleDao.save(role);
roleDao.save(role1);
}