A106_springdata jpa_day2

目录

1.内容介绍

1. 集合映射;(掌握)
2. 双向多对一、一对多;(掌握)
3. 单向多对多;(掌握)
4. 双向向多对多;(了解)
5. jppl入门;(掌握)
6. 原生sql查询;(掌握)

2.集合映射 一对多 多对多

JPA提供了集合映射(刚才咱们已经学习了单向一对多,其中使用的Set就是集合映射的一种,而现在,我们再深入的来学习与了解一下集合映射与它的配置方式)

2.1.hibernate提供的集合延迟加载类

这里大家要理解一个概念,就是Hibernate为我们提供了相应的集合延迟加载类来实现的懒加载,这个和咱们之前学习的关联关系对象的延迟加载是一个道理。
但是,集合的类型常用就2种,我们可以看一下有哪些? 并用咱们刚刚学习过的Set来举例说明一下

Hibernate提供的类:实现延迟加载的关键类

org.hibernate.collection.internal.PersistentSet 实现了java.util.Set接口
org.hibernate.collection.internal.PersistentBag 实现了java.util.List接口

上面只有Set我们是学过的,因此:我们先使用Set来证明咱们所说延迟效果。
PersistentBag 还没有学,先不管,马上等会再说。

2.2.测试代码
@GetMapping("/{id}")
public Department getById(@PathVariable(name = "id") Long id){
    Department department = departmentService.getById(id);
    System.out.println(department);
    System.out.println("===================");
    Set<Employee> employees = department.getEmployees();
    System.out.println(employees.getClass());
    for (Employee employee : employees) {
        System.out.println(employee);
    }
    return department;
}
2.3.声明集合的时候必须使用接口

现在我们思考一个问题:我们现在写代码都是用接口声明,然后再用具体实现类来完成对象的创建(面向接口编程)
例:Set<Product>products = new HashSet<>();

那么在这个地方能改成不用接口的方式吗?,使用使用类来声明嘛?
例:HashSet<Product>products = new HashSet<Product>();

按理来说是可以的,但是如果我们做了这种修改,可以看到,咱们查询的代码就出错。 这又是什么原因呢?

因为Hibernate在创建了集合对象后是使用了它的的集合类PersistentSet来接收数据的(而不是我们写的那个HashSet)。如果我们使用接口声明(如Set),那么它的集合类PersistentSet也是实现了这个接口。所以它可以把这个类赋给Set。 但是我们使用HashSet,它们关系是平级的,不能进行转换,所以就出现错误了。

结论:声明集合的时候必须使用接口

2.4.集合映射的类型
2.4.1.集合映射的JAVA类型

常用的有2种:
java.util.Set对应PersistentSet
java.util.List对应PersistentBag

Set:不能重复,集合里面的元素没有顺序
List:可以重复,集合里面的元素有顺序

2.4.2.怎样选择

怎么选择使用哪一种呢:看数据是否会重复,是否需要顺序
set没有重复,没有顺序,一般用在多对多manytomany,也有一对多 onetomany
list有重复,顺序要求
一般只是用在组合关系 --订单 单据 (什么模型才是组合关系,需要自己分析,这个是需要经验的)
这里我们大致认识一下就行了,接下来,我们会开始来讲这个映射的方式。

2.4.3.Set映射

特点:无序,不可以重复

@OneToMany
@JoinColumn(name = "dir_id")
private Set<ProductSet> products = new HashSet<ProductSet>(); 
2.4.4.List映射

特点:有序,可以重复,一般用在组合关系

@OneToMany
@JoinColumn(name = "dir_id")
@OrderBy("price DESC")       
private List<ProductSet> products = new ArrayList<ProductSet>();  = new HashSet<ProductSet>(); 

默认拿到的List是按照保存的顺序输出的。比如说员工需要根据工资排序,产品需要根据价格排序,这个有办法嘛?
当前是可以的,就是上面配置的@OrderBy(“price DESC”)

@Test
public void get() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();

  // 拿到一方(产品类别)的数据
  ProductDirSet dir = entityManager.find(ProductDirSet.class, 1L);
  // 打印产品类别
  System.out.println(dir);
  // 打印产品类别
  // class org.hibernate.collection.internal.PersistentSet
  // class org.hibernate.collection.internal.PersistentBag
  System.out.println(dir.getProducts().getClass());
  
  List<ProductSet> products = dir.getProducts();
  // 验证排序效果
  for (ProductSet productSet : products) {
   System.out.println(productSet); 
  }
  entityManager.close();
}
2.5.小结

1.配置集合的时候必须配置接口 底层已经不是hashSet,ArrayList等,是hibernate封装好两个类
2.一般都是使用Set接口,只有组合关系使用List接口
3.使用List接口的时候可以配置@Orderby进行排序

3.双向多对一,一对多

3.1.配置
部门 一方

//对于多的处理,默认是懒加载
@OneToMany(
        mappedBy = "department")
private Set<Employee> employees = new HashSet<>();

员工:多方
@ManyToOne
@JoinColumn(name = "dept_id")
@JsonIgnore //忽略属性,防止递归获取,最终导致死循环。
private Department department;

3.2.测试

页面,或者改为立即加载

@Test
public void get(){
    Department department = departmentService.getById(4L);
    System.out.println(department);
    System.out.println(department.getEmployees());

}
3.3.级联操作
3.3.1.级联类型

· CascadeType.PERSIST
级联保存,保存当前实体时,与它有映射关系的实体也会跟着被保存。
· CascadeType.REMOVE
Cascade remove operation,级联删除操作。
删除当前实体时,与它有映射关系的实体也会跟着被删除。
· CascadeType.MERGE
Cascade merge operation,级联更新(合并)操作。
当Student中的数据改变,会相应地更新Course中的数据。
· CascadeType.DETACH
Cascade detach operation,级联脱管/游离操作。
如果你要删除一个实体,但是它有外键无法删除,你就需要这个级联权限了。它会撤销所有相关的外键关联。
· CascadeType.REFRESH
Cascade refresh operation,级联刷新操作。
假设场景 有一个订单,订单里面关联了许多商品,这个订单可以被很多人操作,那么这个时候A对此订单和关联的商品进行了修改,与此同时,B也进行了相同的操作,但是B先一步比A保存了数据,那么当A保存数据的时候,就需要先刷新订单信息及关联的商品信息后,再将订单及商品保存。(来自良心会痛的评论)
· CascadeType.ALL
Cascade all operations,清晰明确,拥有以上所有级联操作权限。

3.3.2.级联保存
//对于多的处理,默认是懒加载
@OneToMany(cascade = CascadeType.PERSIST,
        mappedBy = "department")
private Set<Employee> employees = new HashSet<>();
/**
 *
 */
@Test
public void test() {
    // 1.mappedBy = "dir"表示一方的关系参照多方Prodcut属性dir来管理
    // 2.cascade = CascadeType.PERSIST
    // 3.必须2边都建立关系
    // 4.entityManager.persist(dir);只能保存一方
    Department department = new Department("xxx");

    Employee employee = new Employee("ls");
    Employee employee1 = new Employee("ww");
    department.getEmployees().add(employee);
    department.getEmployees().add(employee1);

    employee.setDepartment(department);
    employee1.setDepartment(department);

    departmentService.add(department);
3.3.3.级联删除

1)不能直接删除-没有配置级联-有外键
Cannot delete or update a parent row: a foreign key constraint fails
2)级联删除

      //对于多的处理,默认是懒加载
    @OneToMany(cascade = CascadeType.REMOVE,
            mappedBy = "department")
    private Set<Employee> employees = new HashSet<>();

3)解除关系删除

// @OneToMany(mappedBy = "department",orphanRemoval=true)
// orphanRemoval=true可以从一方删除解除关系(就是外键dept_id=null)的多方
3.3.4.小结

4.单向多对多 员工对角色

4.1.基本配置

Employee

@ManyToMany
private Set<Role> roles = new HashSet<>();

生成表测试

4.2.详细配置
// @ManyToMany注释表示Teacher是多对多关系的一端。
// @JoinTable描述了多对多关系的数据表关系。name属性指定中间表名称,joinColumns定义中间表与Teacher表的外键关系。
@ManyToMany
@JoinTable(name = "t_employee_role",joinColumns = {@JoinColumn(name = "employee_id")}
,inverseJoinColumns = {@JoinColumn(name = "role_id")})//指定表名
private Set<Role> roles = new HashSet<>();

生成表测试

4.3.保存
  @Test //添加员工并分配角色
public void add() {
    Employee employee = new Employee("ly");

    Role role = new Role();
    role.setId(1L);


    employee.getRoles().add(role);

    employeeService.add(employee);
}

@Test //删除员工,正常来说,不能删除,有外键关联
public void del() {
    employeeService.del(2L);
}


@Test //删除员工,正常来说,不能删除,有外键关联
public void get() {
    Employee employee = employeeService.getById(3L);
    System.out.println(employee);
    for (Role role : employee.getRoles()) {
        System.out.println(role);
    }
}

5.双向多对多

5.1.配置
员工
//多对多
@ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.PERSIST) //默认表名t_employee_roles employee_id roles_id
//修改表名和字段名 name中间表名 joinColumns己方要加入字段,inverseJoinColumns对方所要加入字段

@JoinTable(name = "t_employee_role",
        joinColumns = {@JoinColumn(name = "employee_id")},
        inverseJoinColumns = {@JoinColumn(name = "role_id")}
        )
private Set<Role> roles = new HashSet<>();
@ManyToMany(mappedBy = "roles")
@JsonIgnore
private Set<Employee> employees = new HashSet<>();

查询测试

5.2.级联操作

一般不会做级联

@Test
public void add1() {

    Employee employee1 = new Employee("e1");

    Role role1 = new Role("r1");
    Role role2 = new Role("r2");

    employee1.getRoles().add(role1);
    employee1.getRoles().add(role2);

    role1.getEmployees().add(employee1);
    role2.getEmployees().add(employee1);


    employeeService.add(employee1);

}

@Test
public void get1() {

    Employee employee = employeeService.getById(1L);
    System.out.println(employee);
    for (Role role : employee.getRoles()) {
        System.out.println(role);
    }
}

6.Jpql入门

JPA的查询语句

6.1.入门需知
6.1.1.最基本的JPQL的格式

只能写java的类名和属性名

SELECT o[o.property,o.property*] FROM Entity o
[WHERE conditions] 
[GROUP BY conditions]
[HAVING conditions]
[ORDER BY o.property[ASC|DESC]]

JPQL本质是JPA通过antlr-2.7.7.jar翻译成sql并且封装执行的。

6.1.2.学JPQL记住两个点

1.JPQL和SQL很像,查询关键字都是一样的
2.唯一的区别是:JPQL是面向对象的

6.1.3.JPQL书写规则

JPA的查询语言,类似于sql
1.里面不能出现表名,列名,只能出现java的类名,属性名,区分大小写
2.出现的sql关键字是一样的意思,不区分大小写
3.不能写select * 要写select 别名

6.2.简单查询
6.2.1.查询所有员工【查询实体类型】
@Test
public void test1() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select o from Employee o";
  Query query = entityManager.createQuery(jpql);
  List<Employee> list = query.getResultList();
  for (Employee employee : list) {
    System.out.println(employee);
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
6.2.2.查询所有员工的姓名和所属部门名称【查询特定属性】
@Test
public void test2() throws Exception {
	  EntityManager entityManager = JPAUtils.getEntityManager();
	  //代码(只有一个参数的话可以使用List<String>接收数据)  
	  //代码(多个参数可以使用List<Object[]>接收数据)
	  String jpql = "select o.name,o.department.name from Employee o";
	  Query query = entityManager.createQuery(jpql);
	  List<Object[]> list = query.getResultList();
	  for (Object[] objects : list) {
	    System.out.println(Arrays.toString(objects));
	  }
	  System.out.println("size:" + list.size());
	  entityManager.close();
}
@Test
public void test21() throws Exception {
	  EntityManager entityManager = JPAUtils.getEntityManager();
	  //代码(SQL中使用对象封装参数)
	  String jpql = "select new Employee(o.name,o.department.name) from Employee o";
	  Query query = entityManager.createQuery(jpql);
	  // 注意:使用这种方式我们需要提供相应的构造方法
	  // public Employee() {}
	  // public Employee(String name,String deptName) {
	  // 	this.name = name;
	  // 	this.dept = new Department();
	  // 	this.dept.setName(deptName);
	  // }
	  List<Employee> list = query.getResultList();
	  for (Employee employee : list) {
	    System.out.println(employee.getName() + "," + employee.getDepartment().getName());
	  }
	  System.out.println("size:" + list.size());
	  entityManager.close();
}
6.2.3.查询出所有在成都和广州工作的员工【查询结果过滤】
@Test
public void test3() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select o from Employee o where o.department.city=? or o.department.city=?";
  Query query = entityManager.createQuery(jpql);
  query.setParameter(1, "成都").setParameter(2, "广州");
  List<Employee> list = query.getResultList();
  for (Employee employee : list) {
    System.out.println(employee);
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
6.2.4.查询出所有员工信息,按照月薪排序【查询排序】
@Test
public void test4() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select o from Employee o order by o.salary desc";
  Query query = entityManager.createQuery(jpql);
  List<Employee> list = query.getResultList();
  for (Employee employee : list) {
    System.out.println(employee);
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
6.2.5.查询出所有员工信息,按照部门编号排序【使用关联对象属性排序】
@Test
public void test5() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select o from Employee o order by o.department.id desc";
  Query query = entityManager.createQuery(jpql);
  List<Employee> list = query.getResultList();
  for (Employee employee : list) {
    System.out.println(employee);
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
6.2.6.查询出在恩宁路和八宝街上班的员工信息【使用IN】
代码:使用占位符
@Test
public void test6() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select o from Employee o where o.department.street in(?0,?1)";
  Query query = entityManager.createQuery(jpql);
  query.setParameter(0, "恩宁路").setParameter(1, "八宝街");
  List<Employee> list = query.getResultList();
  for (Employee employee : list) {
    System.out.println(employee);
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
6.2.7.查询出工资在5000-6000的员工【使用BETWEEN…AND…】
命名参数查询
@Test
public void test7() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select o from Employee o where o.salary between :min and :max";
  Query query = entityManager.createQuery(jpql);
  query.setParameter("min", new BigDecimal(5000)).setParameter("max", new BigDecimal(6000));
  List<Employee> list = query.getResultList();
  for (Employee employee : list) {
    System.out.println(employee);
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
6.2.8.查询出姓名包含er或者en的员工【使用LIKE】
@Test
public void test8() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select o from Employee o where o.name like ? or o.name like ?";
  Query query = entityManager.createQuery(jpql);
  query.setParameter(1, "%er%").setParameter(2, "%en%");
  List<Employee> list = query.getResultList();
  for (Employee employee : list) {
    System.out.println(employee);
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
6.3.distinct去重 了解
1.1.1.查询出有员工的部门【distinct】
@Test
public void test9() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select distinct o.department from Employee o";
  Query query = entityManager.createQuery(jpql);
  List<Department> list = query.getResultList();
  for (Department department : list) {
    System.out.println(department);
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
6.4.集合的操作(size)了解

集合在JPA中经常出现,对集合的操作(size)
sql里面没有size(最终换成sql的count查询)
注意:使用size就是操作集合,那么我们就必需配置员工与部分双向的关系,让部门也可以找到对应的员工

1.1.2.查询出有员工的部门【size】//必须配置双向一对多:部门和员工
// 一对多:一个部门里面有多个员工
@OneToMany(mappedBy = "department")
private Set<Employee> employees = new HashSet<Employee>();
getter,setter

@Test
public void test10() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select o from Department o where o.employees.size>0";
  Query query = entityManager.createQuery(jpql);
  List<Department> list = query.getResultList();
  for (Department department : list) {
    System.out.println(department);
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
1.1.3.查询出部门信息,按照部门的员工人数排序
@Test
public void test11() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select o from Department o order by o.employees.size desc";
  Query query = entityManager.createQuery(jpql);
  List<Department> list = query.getResultList();
  for (Department department : list) {
    System.out.println(department);
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
1.1.4.查询出没有员工参与的项目【对集合使用size】
@Test
public void test12() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select o from Project o where o.employees.size=0";
  Query query = entityManager.createQuery(jpql);
  List<Project> list = query.getResultList();
  for (Project project : list) {
    System.out.println(project);
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
6.5.JOIN 了解
JPA中的JOIN和LEFT JOIN(使用SQL/JPQL对比)
sql:select * 表1 join 表2 on 条件
jpql:
     1.不写on子句
     2.模型 模型的别名 join 写前面模型别名.出来的对象属性
    Employee e join e.department d 
1.1.5.查询出所有员工及部门名称【JOIN/LEFT JOIN】
@Test
public void test13() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select o,d.name from Employee o left join o.department d";
  Query query = entityManager.createQuery(jpql);
  List<Object[]> list = query.getResultList();
  for (Object[] objects : list) {
    System.out.println(Arrays.toString(objects));
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
1.1.6.查询出市场部员工信息及电话
@Test
public void test14() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select e,o from Phone o join o.employee e where e.department.name=?";
  Query query = entityManager.createQuery(jpql);
  query.setParameter(1, "市场部");
  List<Object[]> list = query.getResultList();
  for (Object[] objects : list) {
    System.out.println(Arrays.toString(objects));
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
6.6.聚集(组)函数/GROUP/HAVING 了解
1.1.7.查询出各个部门员工的平均工资和最高工资【使用聚集函数】
@Test
public void test15() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select avg(o.salary),max(o.salary) from Employee o group by o.department.name";
  Query query = entityManager.createQuery(jpql);
  List<Object[]> list = query.getResultList();
  for (Object[] objects : list) {
    System.out.println(Arrays.toString(objects));
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
1.1.8.查询出各个项目参与人数报表
// 项目1 3人
// 项目2 2人
@Test
public void test16() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  //查询出各个项目参与人数(不使用聚合)
  String jpql = "select o.name,o.employees.size from Project o where o.employees.size>0";
  //查询出各个项目参与人数(使用聚合)
  jpql = "select o.name,count(e) from Project o join o.employees e group by o.name";
  Query query = entityManager.createQuery(jpql);
  List<Object[]> list = query.getResultList();
  for (Object[] objects : list) {
    System.out.println(Arrays.toString(objects));
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
6.7.子查询:查询出大于平均工资的员工信息 了解
@Test
public void test17() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select o from Employee o where salary>(select avg(salary) from Employee)";
  Query query = entityManager.createQuery(jpql);
  List<Employee> list = query.getResultList();
  for (Employee employee : list) {
    System.out.println(employee);
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}
6.8.分页
6.8.1.查询总数
@Test
public void count() throws Exception {
  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select count(o) from Employee o";
  Query query = entityManager.createQuery(jpql);
  Long result = (Long) query.getSingleResult();
  System.out.println("size:" + result);
  entityManager.close();
}
6.8.2.获取分页数据
@Test
public void limit() throws Exception {
  // 当前页码
  int currentPage = 2;
  // 一页显示条数
  int pageSize = 5;

  EntityManager entityManager = JPAUtils.getEntityManager();
  String jpql = "select o from Employee o";
  Query query = entityManager.createQuery(jpql);
  // 从那里开始取数据,索引从0开始
  int firstResult = (currentPage - 1) * pageSize;
  // 取多少条
  int maxResults = pageSize;
  query.setFirstResult(firstResult).setMaxResults(maxResults);
  List<Employee> list = query.getResultList();
  for (Employee employee : list) {
    System.out.println(employee);
  }
  System.out.println("size:" + list.size());
  entityManager.close();
}

7.原生sql查询

7.1.支持机制
EntityManager 
//代码
Em.close();
7.2.常见使用方式

1)返回数组

	Query query = entityManager.createNativeQuery(sql);
    List<Object[]> list = query.getResultList();

2)返回模型对象

	Query query = entityManager.createNativeQuery(sql, Employee.class);
    List<Employee> list = query.getResultList();

3)返回查询条件

 	Query query = entityManager.createNativeQuery(sql, Employee.class);
    query.setParameter(1, "%en%");
    List<Employee> list = query.getResultList();

8. 课程总结

8.1.重点
8.2.难点
8.3.如何掌握?

1.多多理解
2.学会看说明手册

8.4.排错技巧(技巧)

1…
2…

9.课后练习

1.总结
2.课堂作业

10.面试题

11.扩展知识或课外阅读推荐(可选)

11.1.扩展知识
11.2.课外阅读

微服务那点儿事情
https://blog.csdn.net/w05980598/article/details/79007194/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值