由于企业开发时,搜索的条件内容个数不确定,使用Specifications动态查询解决此问题
看一下第一到第四个视频
一、Specifications动态查询
JpaSpecificationExecutor 方法列表
T findOne(Specification spec); //查询单个对象
List findAll(Specification spec); //查询列表
//查询全部,分页
//pageable:分页参数
//返回值:分页pageBean(page:是springdatajpa提供的)
Page findAll(Specification spec, Pageable pageable);
//查询列表
//Sort:排序参数
List findAll(Specification spec, Sort sort);
long count(Specification spec);//统计查询
Specification :查询条件
自定义我们自己的Specification实现类
实现
//root:查询的根对象(查询的任何属性都可以从根对象中获取)
//CriteriaQuery:顶层查询对象,自定义查询方式(了解:一般不用)
//CriteriaBuilder:查询的构造器,封装了很多的查询条件
Predicate toPredicate(Root root, CriteriaQuery<?> query, CriteriaBuilder cb); //封装查询条件
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:META-INF/persistence.xml")
public class TestSpec {
@Autowired
CustomerDao customerDao;
/**
* 自定义查询条件
* 1.实现Specification接口(提供泛型:查询的对象类型)
* 2.实现toPredicate方法(构造查询条件)
* 3.需要借助方法参数中的两个参数(
* root:获取需要查询的对象属性
* CriteriaBuilder:构造查询条件的,内部封装了很多的查询条件(模糊匹配,精准匹配)
* )
* 案例:根据客户名称查询,查询客户名为传智播客的客户
* 查询条件
* 1.查询方式
* cb对象
* 2.比较的属性名称
* root对象
*
*/
@Test
public void test01(){
Specification<Customer> spec =new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//获取比较的属性
Path<Object> custAddress = root.get("custAddress");
//第一个参数是指定比较属性的属性名
//第二个参数是指定具体精准查询的值
Predicate p = cb.equal(custAddress, "三道沟");
return p;
}
};
Customer one = customerDao.findOne(spec);
System.out.println(one);
}
/**
* 多个精准条件查询
*/
@Test
public void test02(){
Specification<Customer> spec =new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path<Object> custAddress = root.get("custAddress");
Path<Object> custLevel = root.get("custLevel");
Predicate p1 = cb.equal(custAddress, "三道沟");
Predicate p2 = cb.equal(custLevel, "22");
//以与的形式,进行查询 cb.or 以或的形式查询
Predicate pp = cb.and(p1, p2);
return pp;
}
};
List<Customer> all = customerDao.findAll(spec);
System.out.println(all);
}
/**
* 案例:完成根据客户名称的模糊匹配,返回客户列表
* 客户名称以 ’传智播客‘ 开头
* <p>
* equal :直接的到path对象(属性),然后进行比较即可
* gt,lt,ge,le,like : 得到path对象,根据path对象指定比较的参数类型,再去进行比较
* 指定参数类型:path.as(类型的字节码对象)
*/
@Test
public void test03(){
Specification<Customer> spec =new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path<Object> custAddress = root.get("custAddress");
Predicate like = cb.like(custAddress.as(String.class), "三%");
return like;
}
};
List<Customer> all = customerDao.findAll(spec);
System.out.println(all);
//添加排序
//创建排序对象,需要调用构造方法实例化sort对象
//第一个参数:排序的顺序(倒序,正序)
// Sort.Direction.DESC:倒序
// Sort.Direction.ASC : 升序
//第二个参数:排序的属性名称
//Sort sort = new Sort(Sort.Direction.DESC, "custId");
//List<Customer> list = customerDao.findAll(spec, sort);
//for (Customer customer : list) {
// System.out.println(customer);
//}
}
/**
* 分页查询
* Specification: 查询条件
* Pageable:分页参数
* 分页参数:查询的页码,每页查询的条数
* findAll(Specification,Pageable):带有条件的分页
* findAll(Pageable):没有条件的分页
* 返回:Page(springDataJpa为我们封装好的pageBean对象,数据列表,共条数)
*/
@Test
public void test04(){
Specification<Customer> spec= new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path<Object> custAddress = root.get("custAddress");
Predicate p1 = cb.equal(custAddress, "三道沟");
return p1;
}
};
//PageRequest对象是Pageable接口的实现类
/**
* 创建PageRequest的过程中,需要调用他的构造方法传入两个参数
* 第一个参数:当前查询的页数(从0开始)
* 第二个参数:每页查询的数量
*/
Pageable p =new PageRequest(0,4);//不带排序的查询的分页
Sort s = new Sort(Sort.Direction.DESC,"custId");//排序对象
Pageable p2 = new PageRequest(0,4,s);//带排序的查询的分页
//Page<Customer> page = customerDao.findAll(spec, p);//带条件的查询
Page<Customer> all = customerDao.findAll(p2);//不带条件的查询
//page.getContent();//得到数据列表
//page.getTotalPages();//得到总页数
//page.getTotalElements();//得到总页数
System.out.println(all.getContent());
}
}
二、多表之间的关系和操作多表的操作步骤
i、表关系
**一对一:**
**一对多:**
一的一方:主表
多的一方:从表
外键:需要再从表上新建一列作为外键,他的取值来源于主表的主键
**多对多:**
中间表:中间表中最少应该由两个字段组成,这两个字段做为外键指向两张表的主键,又组成了联合主键
讲师对学员:一对多关系
ii、实体类中的关系
包含关系:可以通过实体类中的包含关系描述表关系
继承关系
iii、分析步骤
1.明确表关系
2.确定表关系(描述 外键|中间表)
3.编写实体类,再实体类中描述表关系(包含关系)
4.配置映射关系
三、完成多表操作
i.一对多操作
1)、主表的实体类
//配置客户和联系人之间的关系(一对多关系)
/**
* 使用注解的形式配置多表关系
* 1.声明关系
* @OneToMany :配置一对多的关系
* targetEntity :对方对象的字节码对象
* 2.配置外键(中间表)
* @JoinColumn:配置外键
* name: 外键字段名称
* referencedColumnName:参照主表的主键字段名称
* 在客户实体类上(一的一方)添加了外键的配置,所以对于客户而言,也具备了维护外键的作用
*/
@OneToMany(targetEntity = LinkMan.class)
//name =“外键” referencedColumnName =“主键”
@JoinColumn(name = "lkm_cust_id",referencedColumnName ="cust_id" )
private Set<LinkMan> linkMens = new HashSet<LinkMan>();
2)、从表的实体类
/**
* 配置联系人到客户的多对一关系
* 使用注解的形式配置多对一关系
* 1.配置表关系
* @ManyToOne:配置多对一的关系
* targetEntity:对方的实体类字节码
* 2.配置外键(表)
* 配置外键的过程,配置到了多的一方,就会在多的一方维护外键
*/
@ManyToOne(targetEntity = Customer.class)
//name =“外键” referencedColumnName =“主键”
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private Customer customer;
xml中需要添加内容
<!-- 2.配置entityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--注入jpa的配置信息
加载jpa的基本配置信息和jpa实现方式(hibernate)的配置信息
hibernate.hbm2ddl.auto:自动创建数据库表
create:每次都会重新创建数据库表
update:有表不会重新创建,没有表会重新创建表-->
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:META-INF/persistence.xml")
public class OneToMany {
@Autowired
CustomerDao customerDao;
@Autowired
LinkManDao linkManDao;
/**
* 保存一个客户,保存一个联系人
* 问题是:多表的外键为空???
* 原因是:在保存的时候,两张表没有产生关系
*/
@Test
@Transactional
@Rollback(false)//不加Rollback的话,springdata JPA 会默认自动回滚
public void saveDemo1(){
Customer customer =new Customer();
customer.setCustName("小程程");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("小鹏鹏");
//在保存的时候,让两张表产生关系
//customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);
customerDao.save(customer);
}
/**
* 如果两张表,保存的时候,互相都产生关系会发生什么?
*如果两个都添加的话,会多出下面的一条SQL语句
* update cst_linkman set lkm_cust_id=? where lkm_id=?
* 解决办法是:只需要在一的一方放弃维护权即可
*/
@Test
@Transactional
@Rollback(false)//不加Rollback的话,springdata JPA 会默认自动回滚
public void saveDemo2(){
Customer customer =new Customer();
customer.setCustName("小程程");
LinkMan linkMan1 = new LinkMan();
linkMan1.setLkmName("小鹏鹏");
/**
* 配置了两张表保存时的关系
* 在多表那一方添加
*/
customer.getLinkMans().add(linkMan1);
/**
*也可以在一表那一方添加
*/
//linkMan1.setCustomer(customer);
customerDao.save(customer);
linkManDao.save(linkMan1);
}
}
解决办法是:只需要在一的一方放弃维护权即可
/**
* 放弃维护权
* mappedBy= 对方配置关系的属性名称
*/
@OneToMany(mappedBy = "customer")
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
级联:
操作一个对象的同时操作他的关联对象
级联操作:
1.需要区分操作主体
2.需要在操作主体的实体类上,添加级联属性(需要添加到多表映射关系的注解上)
3.cascade(配置级联)
级联添加
案例:当我保存一个客户的同时保存联系人
级联删除
案例:当我删除一个客户的同时删除此客户的所有联系人
/**
* 级联操作
* 级联添加
* 需要在主表的关系注解上加cascade = CascadeType.ALL
* @OneToMany(mappedBy = "customer" ,cascade = CascadeType.ALL)
* private Set<LinkMan> linkMans = new HashSet<LinkMan>();
*/
@Test
@Transactional
@Rollback(false)//不加Rollback的话,springdata JPA 会默认自动回滚
public void saveDemo3(){
Customer customer =new Customer();
customer.setCustName("小冉冉");
LinkMan linkMan1 = new LinkMan();
linkMan1.setLkmName("小胖胖");
/**
* 配置了两张表保存时的关系
* 在多表那一方添加
*/
customer.getLinkMans().add(linkMan1);
/**
*也可以在一表那一方添加
*/
linkMan1.setCustomer(customer);
customerDao.save(customer);
}
/**
* 级联操作
* 级联删除
* 需要在主表的关系注解上加cascade = CascadeType.ALL
* cascade是在多表的关系的注解上都可以加
* @OneToMany(mappedBy = "customer" ,cascade = CascadeType.ALL)
* private Set<LinkMan> linkMans = new HashSet<LinkMan>();
*/
@Test
@Transactional
@Rollback(false)//不加Rollback的话,springdata JPA 会默认自动回滚
public void saveDemo4(){
//查询1号客户
Customer one = customerDao.findOne(7l);
//删除1号客户
customerDao.delete(one);
}
ii.多对多操作
案例:用户和角色(多对多关系)
用户:
角色:
分析步骤
1.明确表关系
多对多关系
2.确定表关系(描述 外键|中间表)
中间间表
3.编写实体类,再实体类中描述表关系(包含关系)
用户:包含角色的集合
角色:包含用户的集合
4.配置映射关系
dao层接口
//role 的接口
public interface RoleDao extends JpaRepository<Role,Long>, JpaSpecificationExecutor<Role> {
}
//user 的接口
public interface UserDao extends JpaRepository<User,Long>, JpaSpecificationExecutor<User> {
}
实体类
//Role的实体类
@Entity
@Table(name = "sys_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private Long roleId;
@Column(name = "role_name")
private String roleName;
/**
* 被动的一方放弃维护权,mappedBy = 对方中本类的属性名称
* @ManyToMany(mappedBy = "roles")
*/
@ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<User>();
省去getter 和 setter。。。。
//User的实体类
@Entity
@Table(name = "sys_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="user_id")
private Long userId;
@Column(name="user_name")
private String userName;
@Column(name="age")
private Integer age;
//添加Role集合
// 2-2
/**
* 1. 确定关系注解
* 2. 确定由哪方维护外键关系
* 3. 在维护关系一方,添加外键关系信息
*
* 声明表关系 targetEntity = Role.class 指向对方实体类的字节码对象
* @ManyToMany(targetEntity = Role.class)
* 级联操作
* @ManyToMany(cascade = CascadeType.ALL)
*
* JoinTable:中间表名
* joinColumns:本方在中间表的外键名
* inverseJoinColumns:对方在中间表的外键名
*/
// @ManyToMany(targetEntity = Role.class)
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "role_user",joinColumns = {@JoinColumn(name = "user_id"/*,referencedColumnName = "user的主键"*/)},inverseJoinColumns = {@JoinColumn(name = "role_id"/*,referencedColumnName = "role的主键"*/)})
Set<Role> roles = new HashSet<Role>();
//省去getter 和setter方法....
测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:META-INF/persistence.xml")
public class TestSpec {
@Autowired
private UserDao userDao;
@Autowired
private RoleDao roleDao;
/**
* 保存一个用户,保存一个角色
*
* 多对多放弃维护权:被动的一方放弃
*/
@Test
@Transactional
@Rollback(false)
public void testAdd() {
User user = new User();
user.setUserName("zs");
Role role = new Role();
role.setRoleName("管理员");
//1.建立关系
user.getRoles().add(role);
role.getUsers().add(user);
//2.保存User和Role
userDao.save(user);
roleDao.save(role);
}
//测试级联添加(保存一个用户的同时保存用户的关联角色)
@Test
@Transactional
@Rollback(false)
public void testCasCadeAdd() {
User user = new User();
user.setUserName("zs");
Role role = new Role();
role.setRoleName("管理员");
//用户关联角色
user.getRoles().add(role);
//role维护关键
role.getUsers().add(user);
//保存用户
userDao.save(user);
}
/**
* 案例:删除id为1的用户,同时删除他的关联对象
*/
@Test
@Transactional
@Rollback(false)
public void testCasCadeRemove() {
userDao.delete(2L);
}
}
POM文件参照上面
iii.多表的查询
1.对象导航查询
查询一个对象的同时,通过此对象查询他的关联对象
案例:客户和联系人
从一方查询多方
* 默认:使用延迟加载(****)
从多方查询一方
* 默认:使用立即加载
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:META-INF/persistence.xml")
public class ObjectQueryTest {
@Autowired
private CustomerDao customerDao;
@Autowired
private LinkManDao linkManDao;
//could not initialize proxy - no Session
//测试对象导航查询(查询一个对象的时候,通过此对象查询所有的关联对象)
@Test
//@Transactional // 解决在java代码中的no session问题
public void testQuery1() {
//查询id为1的客户
Customer customer = customerDao.getOne(1l);
//对象导航查询,此客户下的所有联系人
Set<LinkMan> linkMans = customer.getLinkMans();
for (LinkMan linkMan : linkMans) {
System.out.println(linkMan);
}
}
/**
* 对象导航查询:
* 默认使用的是延迟加载的形式查询的
* 调用get方法并不会立即发送查询,而是在使用关联对象的时候才会查询
* 延迟加载!
* 修改配置,将延迟加载改为立即加载
* fetch,需要配置到多表映射关系的注解上(@ManyToOne(fetch = "
* EAGER :立即加载
* LAZY :延迟加载"))
*
*/
@Test
@Transactional // 解决在java代码中的no session问题
public void testQuery2() {
//查询id为1的客户
Customer customer = customerDao.findOne(1l);
//对象导航查询,此客户下的所有联系人
Set<LinkMan> linkMans = customer.getLinkMans();
System.out.println(linkMans.size());
}
/**
* 从联系人对象导航查询他的所属客户
* * 默认 : 立即加载
* 延迟加载:
*
*/
@Test
@Transactional // 解决在java代码中的no session问题
public void testQuery3() {
LinkMan linkMan = linkManDao.findOne(2l);
//对象导航查询所属的客户
Customer customer = linkMan.getCustomer();
System.out.println(customer);
}
/**
* Specification的多表查询
*/
@Test
public void testFind() {
Specification<LinkMan> spec = new Specification<LinkMan>() {
public Predicate toPredicate(Root<LinkMan> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
/**
* Join代表连接查询,通过root对象获取
* JoinType.INNER
* JoinType.LEFT
* JoinType.RIGHT
*/
Join<LinkMan, Customer> join = root.join("customer", JoinType.LEFT);
return cb.like(join.get("custName").as(String.class),"百度");
}
};
List<LinkMan> list = linkManDao.findAll(spec);
for (LinkMan linkMan : list) {
System.out.println(linkMan);
}
linkManDao.findOne(1l);
}
}
POM文件
<properties>
<spring.version>5.0.2.RELEASE</spring.version>
<hibernate.version>5.0.7.Final</hibernate.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<c3p0.version>0.9.1.2</c3p0.version>
<mysql.version>5.1.6</mysql.version>
</properties>
<dependencies>
<!-- junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- spring beg -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring对orm框架的支持包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring end -->
<!-- hibernate beg -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.1.Final</version>
</dependency>
<!-- hibernate end -->
<!-- c3p0 beg -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<!-- c3p0 end -->
<!-- log end -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- spring data jpa 的坐标-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.9.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- el beg 使用spring data jpa 必须引入 -->
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
<!-- el end -->
</dependencies>
</project>