avatar

SpringDataJPA-动态查询和多表查询(三)

Specification动态查询

前提

Dao接口必须继承JpaSpecificationExecutor接口

能够使用动态查询的方法

1
2
3
4
5
6
7
8
9
10
11
12
 public interface JpaSpecificationExecutor<T> {
//根据条件查询一个对象
T findOne(Specification<T> spec);
//根据条件查询集合
List<T> findAll(Specification<T> spec);
//根据条件分页查询
Page<T> findAll(Specification<T> spec, Pageable pageable);
//排序查询查询
List<T> findAll(Specification<T> spec, Sort sort);
//统计查询
long count(Specification<T> spec);
}

环境搭建

创建项目

引入依赖[配置pom.xml]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>cn.itcast</groupId>
<artifactId>jpa-day2</artifactId>
<version>1.0-SNAPSHOT</version>

<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>

创建包

cn.itcast.service

cn.itcast.dao

cn.itcast.domain

创建applicationContext.xml

Spring整合Jpa就两步

​ 1.由spring创建entityManagerFactory

​ 2.使用标签jpa:repositories和spring进行整合

提示:该文件应该在java/main/resource目录下

注意修改数据库密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

<!--S Spring配置[之前课程里的旧东西]-->
<!--配置扫描包-->
<context:component-scan base-package="cn.itcast" ></context:component-scan>
<!--创建数据库连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value="root"></property>
<property name="jdbcUrl" value="jdbc:mysql:///jpa" ></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<!--这里不需要配置
<property name="dataSource" ref="dataSource" />
因为数据源已经配置在entityManagerFactory中了
-->
<property name="entityManagerFactory" ref="entityManagerFactoty"></property>
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="pointcut" expression="execution(* cn.itcast.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
</aop:config>
<!--E Spring配置[之前课程里的旧东西]-->







<!--S Spring整合JPA[今天讲的新配置]-->
<!-- 1.创建entityManagerFactory对象交给spring容器管理-->
<bean id="entityManagerFactoty" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--配置的扫描的包(实体类所在的包) -->
<property name="packagesToScan" value="cn.itcast.domain" />
<!-- jpa的实现厂家 -->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
</property>

<!--jpa的供应商适配器 -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--配置是否自动创建数据库表 -->
<property name="generateDdl" value="false" />
<!--指定数据库类型 -->
<property name="database" value="MYSQL" />
<!--数据库方言:支持的特有语法 -->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
<!--是否显示sql -->
<property name="showSql" value="true" />
</bean>
</property>

<!--jpa的方言 :高级的特性 -->
<property name="jpaDialect" >
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
<property name="jpaProperties" >
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>

<!--2.整合spring dataJpa-->
<jpa:repositories base-package="cn.itcast.dao" transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactoty" ></jpa:repositories>
<!--E Spring整合JPA[今天讲的新配置]-->
</beans>

快速入门

创建表cst_customer

提示:day01已经创建过了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
USE `jpa`;
DROP TABLE IF EXISTS `cst_customer`;

CREATE TABLE `cst_customer` (
`cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
`cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
`cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
`cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
`cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
`cust_address` varchar(128) DEFAULT NULL COMMENT '客户联系地址',
`cust_phone` varchar(64) DEFAULT NULL COMMENT '客户联系电话',
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into `cst_customer`(`cust_id`,`cust_name`,`cust_source`,`cust_industry`,`cust_level`,`cust_address`,`cust_phone`) values (1,'黑马程序员',NULL,'教育',NULL,NULL,NULL),(2,'传智播客',NULL,'教育',NULL,NULL,NULL),(3,'酷丁鱼',NULL,'教育',NULL,NULL,NULL),(4,'传智汇',NULL,'教育',NULL,NULL,NULL),(5,'传智专修学院',NULL,'教育',NULL,NULL,NULL),(6,'谷歌',NULL,'教育',NULL,NULL,NULL);

创建实体类Customer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package cn.itcast.domain;
import javax.persistence.*;

public class Customer {
private Long custId; //客户的主键
private String custName;//客户名称
private String custSource;//客户来源
private String custLevel;//客户级别
private String custIndustry;//客户所属行业
private String custPhone;//客户的联系方式
private String custAddress;//客户地址
public Long getCustId() {
return custId;
}
public void setCustId(Long custId) {
this.custId = custId;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}

public String getCustPhone() {
return custPhone;
}

public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}

public String getCustAddress() {
return custAddress;
}

public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}

@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custLevel='" + custLevel + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custPhone='" + custPhone + '\'' +
", custAddress='" + custAddress + '\'' +
'}';
}
}

通过注解配置映射关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package cn.itcast.domain;

import javax.persistence.*;

@Entity
@Table(name = "cst_customer")
public class Customer {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cust_id")
private Long custId; //客户的主键

@Column(name = "cust_name")
private String custName;//客户名称

@Column(name="cust_source")
private String custSource;//客户来源

@Column(name="cust_level")
private String custLevel;//客户级别

@Column(name="cust_industry")
private String custIndustry;//客户所属行业

@Column(name="cust_phone")
private String custPhone;//客户的联系方式

@Column(name="cust_address")
private String custAddress;//客户地址

public Long getCustId() {
return custId;
}

public void setCustId(Long custId) {
this.custId = custId;
}

public String getCustName() {
return custName;
}

public void setCustName(String custName) {
this.custName = custName;
}

public String getCustSource() {
return custSource;
}

public void setCustSource(String custSource) {
this.custSource = custSource;
}

public String getCustLevel() {
return custLevel;
}

public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}

public String getCustIndustry() {
return custIndustry;
}

public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}

public String getCustPhone() {
return custPhone;
}

public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}

public String getCustAddress() {
return custAddress;
}

public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}

@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custLevel='" + custLevel + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custPhone='" + custPhone + '\'' +
", custAddress='" + custAddress + '\'' +
'}';
}
}

创建CustomerDao接口

1
2
3
4
5
6
7
8
9
10
11
12
13
package cn.itcast.dao;

import cn.itcast.domain.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

/**
* JpaRepository<操作的实体类, 实体类中主键的类型> 接口中提供了简单操作数据库的方法
* JpaSpecificationExecutorr<操作的是实体类>接口中提供了复杂方法,比如多条件和分页查询
*/
public interface CustomerDao extends JpaRepository<Customer,Long>,
JpaSpecificationExecutor<Customer>{
}

编写测试类

需求:查询姓名=李四的用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package cn.itcast.dao;

import cn.itcast.domain.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.persistence.criteria.*;
import java.util.List;

/**
* 针对 CustomerDao 接口的多条件查询演示
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class CustomerDaoSpecificationTest {
@Autowired
private CustomerDao customerDao;

/**
* 根据客户名进行查询
*/
@Test
public void findByCustNameQueryTest(){
//生成的SQL select * forom cst_customer where cust_name = "李四"
Customer customer = customerDao.findOne(new Specification<Customer>() {
@Override
/**
* root : 可以获取要操作的属性
* criteriaBuilder : 构建查询条件
生成的SQL语句 select * from cst_customer where cust_name = "李四"
*/
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
//custName是Customer实体类的属性的名字,不是表中的字段名
Path<Object> custName = root.get("custName");
//添加查询条件 where custName="李四"
Predicate predicate = criteriaBuilder.equal(custName, "李四");
return predicate;
}
});

System.out.println(customer);
}
}

运行结果

单条件查询

请查看1.4内容

多条件查询

需求:查询姓名=李四,电话=1390000000的客户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 多条件查询
*/
@Test
public void manyConditionsQueryTest(){
Customer customer = customerDao.findOne(new Specification<Customer>() {
@Override
/**
* select * from cst_customer where cust_name ="李四" and cust_phone = '139xxx'
*/
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
//条件1:客户名=李四
Predicate p1 = criteriaBuilder.equal(root.get("custName"), "李四");
//条件2:电话=1390000000
Predicate p2 = criteriaBuilder.equal(root.get("custPhone"), "1390000000");

//把两个查询条件连接到一起
////custName="李四" or custPhone="1390000000"
//Predicate p3 = criteriaBuilder.or(p1, p2);

////custName="李四" and custPhone="1390000000"
Predicate p3 = criteriaBuilder.and(p1, p2);


return p3;
}
});
System.out.println(customer);
}

运行结果

模糊匹配

种类

模糊匹配有like,>, < ,>=,<=

对应的方法就是like , gt,lt,ge,le

使用步骤

1.从root中获取要操作的属性

2.调用模糊匹配方法,传入两个参数,第一个是path.as(属性的类型.class),第二个是属性值

测试代码

查询客户名包含传智播客,并且ID>=2的用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void dimQuery(){
Customer cus = customerDao.findOne(new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {

//判断客户名包含传智播客 使用like方法
Predicate p1 = criteriaBuilder.like(root.get("custName").as(String.class), "%传智播客%");
//判断ID>=2 使用ge方法
Predicate p2 = criteriaBuilder.ge(root.get("custId").as(Long.class), 2L);

//custName like "%传智播客%" and custId>=2
return criteriaBuilder.and(p1, p2);
}
});
System.out.println(cus);
}

运行结果

排序

一个排序条件

需求:按照ID降序查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 根据一个条件排序
*/
@Test
public void orderByTest1(){
//根据ID降序排序
Sort sort = new Sort(Sort.Direction.DESC,"custId");
//查询条件传入null 表示 查询全部
List<Customer> all = customerDao.findAll(null, sort);

for (Customer customer : all) {
System.out.println(customer);
}
}

运行结果

多个排序条件

按照电话降序排序,如果电话相同按照ID升序排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 根据多个条件排序
*/
@Test
public void orderByTest2(){
//设置排序条件
Sort.Order o1 = new Sort.Order(Sort.Direction.DESC, "custPhone");
Sort.Order o2 = new Sort.Order(Sort.Direction.ASC, "custId");

ArrayList orders = new ArrayList();
//Order对象添加进去的顺序就是排序顺序
orders.add(o1);
orders.add(o2);

Sort sort = new Sort(orders);//把排序条件设置到Sort对象中
//查询条件传入null 表示 查询全部
//select * from cst_customer order by cust_Phone desc, cust_Id asc
List<Customer> all = customerDao.findAll(null, sort);

for (Customer customer : all) {
System.out.println(customer);
}
}

使用CriteriaQuery完成排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void orderTest3(){
List<Customer> all = customerDao.findAll(new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//设置查询条件,可以不写的
query.where(cb.like(root.get("custName").as(String.class),"%传%"));
query.orderBy(cb.desc(root.get("custPhone")), cb.asc(root.get("custId")));

return query.getRestriction();
}
});

for (Customer customer : all) {
System.out.println(customer);
}
}

运行结果

分页

需求 查询ID=4,5,6的数据

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 分页查询
*/
@Test
public void pageTest(){
//第一个参数表示查询哪页的数据,页码从0开始
//第二个参数表示要查询几条记录
PageRequest pageRequest = new PageRequest(1, 3);
Page<Customer> all = customerDao.findAll(null, pageRequest);

for (Customer customer : all) {
System.out.println(customer);
}
}

运行结果

案例

需求,网页需要一个搜索功能,如果用输入客户名,则可以根据客户名搜索,如果输入电话,则可以根据电话搜索,如果都输入了就根据用户名和电话搜索,问,service层中的方法应该如何实现

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public List<Customer> search(String custName, String custPhone){
List<Customer> customers = customerDao.findAll(new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> list = new ArrayList<>();

//拼接客户名
if(custName!=null && !custName.trim().equals("")){
Predicate p = criteriaBuilder.like(root.get("custName").as(String.class), "%" + custName + "%");
list.add(p);
}

//拼接电话
if(custPhone!=null && !custPhone.trim().equals("")){
Predicate p = criteriaBuilder.like(root.get("custPhone").as(String.class), "%" + custPhone + "%");
list.add(p);
}


//把查询条件用and进行拼接
Predicate p = criteriaBuilder.and(list.toArray(new Predicate[0]));

return p;
}
});

return customers;
}

@Test
public void searchTest(){
List<Customer> customers = search("传", "1");
for (Customer customer : customers) {
System.out.println(customer);
}
}

运行结果

多表查询

一对多和多对一

需求

一个Customer[这里用户表示公司]对应多个联系人

创建表

为了避免和之前的数据产生冲突,在讲多表查询的时候表名和实体类名后面都会加数字进行区分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
DROP TABLE IF EXISTS `cst_customer2`;

CREATE TABLE `cst_customer2` (
`cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
`cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
`cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
`cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
`cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
`cust_address` varchar(128) DEFAULT NULL COMMENT '客户联系地址',
`cust_phone` varchar(64) DEFAULT NULL COMMENT '客户联系电话',
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=117 DEFAULT CHARSET=utf8;


DROP TABLE IF EXISTS `cst_linkman2`;

CREATE TABLE `cst_linkman2` (
`lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
`lkm_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',
`lkm_gender` char(1) DEFAULT NULL COMMENT '联系人性别',
`lkm_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
`lkm_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',
`lkm_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
`lkm_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',
`lkm_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',
`lkm_cust_id` bigint(32) DEFAULT NULL COMMENT '客户id(外键)',
PRIMARY KEY (`lkm_id`),
KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8;

创建实体类及映射关系

客户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package cn.itcast.domain;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;


@Entity
@Table(name="cst_customer2")
public class Customer2 {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="cust_id")
private Long custId;
@Column(name="cust_address")
private String custAddress;
@Column(name="cust_industry")
private String custIndustry;
@Column(name="cust_level")
private String custLevel;
@Column(name="cust_name")
private String custName;
@Column(name="cust_phone")
private String custPhone;
@Column(name="cust_source")
private String custSource;



public Long getCustId() {
return custId;
}

public void setCustId(Long custId) {
this.custId = custId;
}

public String getCustAddress() {
return custAddress;
}

public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}

public String getCustIndustry() {
return custIndustry;
}

public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}

public String getCustLevel() {
return custLevel;
}

public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}

public String getCustName() {
return custName;
}

public void setCustName(String custName) {
this.custName = custName;
}

public String getCustPhone() {
return custPhone;
}

public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}

public String getCustSource() {
return custSource;
}

public void setCustSource(String custSource) {
this.custSource = custSource;
}



@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custAddress='" + custAddress + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custLevel='" + custLevel + '\'' +
", custName='" + custName + '\'' +
", custPhone='" + custPhone + '\'' +
", custSource='" + custSource + '\'' +
'}';
}
}

联系人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package cn.itcast.domain;

import javax.persistence.*;

@Entity
@Table(name = "cst_linkman2")
public class LinkMan2 {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "lkm_id")
private Long lkmId; //联系人编号(主键)
@Column(name = "lkm_name")
private String lkmName;//联系人姓名
@Column(name = "lkm_gender")
private String lkmGender;//联系人性别
@Column(name = "lkm_phone")
private String lkmPhone;//联系人办公电话
@Column(name = "lkm_mobile")
private String lkmMobile;//联系人手机
@Column(name = "lkm_email")
private String lkmEmail;//联系人邮箱
@Column(name = "lkm_position")
private String lkmPosition;//联系人职位
@Column(name = "lkm_memo")
private String lkmMemo;//联系人备注



public Long getLkmId() {
return lkmId;
}

public void setLkmId(Long lkmId) {
this.lkmId = lkmId;
}

public String getLkmName() {
return lkmName;
}

public void setLkmName(String lkmName) {
this.lkmName = lkmName;
}

public String getLkmGender() {
return lkmGender;
}

public void setLkmGender(String lkmGender) {
this.lkmGender = lkmGender;
}

public String getLkmPhone() {
return lkmPhone;
}

public void setLkmPhone(String lkmPhone) {
this.lkmPhone = lkmPhone;
}

public String getLkmMobile() {
return lkmMobile;
}

public void setLkmMobile(String lkmMobile) {
this.lkmMobile = lkmMobile;
}

public String getLkmEmail() {
return lkmEmail;
}

public void setLkmEmail(String lkmEmail) {
this.lkmEmail = lkmEmail;
}

public String getLkmPosition() {
return lkmPosition;
}

public void setLkmPosition(String lkmPosition) {
this.lkmPosition = lkmPosition;
}

public String getLkmMemo() {
return lkmMemo;
}

public void setLkmMemo(String lkmMemo) {
this.lkmMemo = lkmMemo;
}

@Override
public String toString() {
return "LinkMan{" +
"lkmId=" + lkmId +
", lkmName='" + lkmName + '\'' +
", lkmGender='" + lkmGender + '\'' +
", lkmPhone='" + lkmPhone + '\'' +
", lkmMobile='" + lkmMobile + '\'' +
", lkmEmail='" + lkmEmail + '\'' +
", lkmPosition='" + lkmPosition + '\'' +
", lkmMemo='" + lkmMemo + '\'' +
'}';
}
}

一对多关系配置及测试

使用一对多的好处:

​ 通过一的一方可以直接获取多的一方

​ 保存一的一方的时候多的一方也会被顺便保存

具体使用方式请查看测试类

1、说明

客户是一的一方

联系人是多的一方

2.在客户实体类(Customer2)中添加集合属性,存储所有的联系人

1
2
3
4
5
6
7
8
9
private Set<LinkMan2> linkMan2s = new HashSet<>();

public Set<LinkMan2> getLinkMan2s() {
return linkMan2s;
}

public void setLinkMan2s(Set<LinkMan2> linkMan2s) {
this.linkMan2s = linkMan2s;
}

3.配置映射关系·

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @OneToMany 说明是一对多关系
* targetEntity:关联指定的实体类
* @JoinColumn 配置外键
* name 副表的外键
* referencedColumnName 主表的主键
*/
@OneToMany(targetEntity = LinkMan2.class)
@JoinColumn(name="lkm_cust_id",referencedColumnName = "cust_id")
private Set<LinkMan2> linkMan2s = new HashSet<>();

public Set<LinkMan2> getLinkMan2s() {
return linkMan2s;
}

public void setLinkMan2s(Set<LinkMan2> linkMan2s) {
this.linkMan2s = linkMan2s;
}

4.创建Dao接口

1
2
3
4
5
6
7
8
9
package cn.itcast.dao;

import cn.itcast.domain.Customer;
import cn.itcast.domain.Customer2;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface CustomerDao2 extends JpaRepository<Customer2,Long>,JpaSpecificationExecutor<Customer2> {
}
1
2
public interface LinkManDao2 extends JpaRepository<LinkMan2,Long>,JpaSpecificationExecutor<LinkMan2> {
}

5.测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package cn.itcast.dao;

import cn.itcast.domain.Customer2;
import cn.itcast.domain.LinkMan2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class One2ManyTest {
@Autowired
private CustomerDao2 customerDao2 ;
@Autowired
private LinkManDao2 linkManDao2;
@Test
@Transactional
@Rollback(false)
/**
* 测试保存
*/
public void add(){
LinkMan2 lm1 = new LinkMan2();
lm1.setLkmEmail("kefuA@foxmail.com");
lm1.setLkmGender("男");
lm1.setLkmMobile("110");
lm1.setLkmName("客服A");

LinkMan2 lm2 = new LinkMan2();
lm2.setLkmEmail("kefuB@foxmail.com");
lm2.setLkmGender("女");
lm2.setLkmMobile("119");
lm2.setLkmName("客服B");

Customer2 customer2 = new Customer2();
customer2.setCustName("JACK");

customer2.getLinkMan2s().add(lm1);//通过客户实体类维护和联系人之间的关系
customer2.getLinkMan2s().add(lm2);//通过客户实体类维护和联系人之间的关系
customerDao2.save(customer2);//保存客户


linkManDao2.save(lm1);//保存联系人
linkManDao2.save(lm2);//保存联系人
}
}

运行结果

多对一关系配置及测试

1.说明

客户(Customer2)[主表]是一的一方

联系人(LinkMan2)[副表]是多的一方

我们需要在多的一方中配置关系

2.在LinkMan2中添加属性CUstomer2

1
2
3
4
5
6
7
8
9
private Customer2 customer2;

public Customer2 getCustomer2() {
return customer2;
}

public void setCustomer2(Customer2 customer2) {
this.customer2 = customer2;
}

3.添加映射关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
targetEntity 指定要关联的实体类
@JoinColumn 配置外键
name 副表中外键的名字
referencedColumnName 主表中主键的名字
*/
@ManyToOne(targetEntity = Customer2.class)
@JoinColumn(name="lkm_cust_id",referencedColumnName = "cust_id")
private Customer2 customer2;

public Customer2 getCustomer2() {
return customer2;
}

public void setCustomer2(Customer2 customer2) {
this.customer2 = customer2;
}

4.测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package cn.itcast.dao;

import cn.itcast.domain.Customer2;
import cn.itcast.domain.LinkMan2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Many2OneTest {
@Autowired
private CustomerDao2 customerDao2;
@Autowired
private LinkManDao2 linkManDao2;

@Test
public void findAllTest(){
List<LinkMan2> all = linkManDao2.findAll();
for (LinkMan2 linkMan2 : all) {
Customer2 customer2 = linkMan2.getCustomer2();
System.out.println(customer2);
}
}
@Test
@Transactional
@Rollback(false)
public void addTest(){
LinkMan2 lm = new LinkMan2();
lm.setLkmEmail("kefuB@foxmail.com");
lm.setLkmGender("女");
lm.setLkmMobile("119");
lm.setLkmName("客服BBBB");

Customer2 customer2 = new Customer2();
customer2.setCustName("JACKBBB");
lm.setCustomer2(customer2);//设置关系
customerDao2.save(customer2);
linkManDao2.save(lm);

}
}

运行结果

放弃关系维护

为什么要放弃关系维护

在多对一的情况下保存用户和客户只需要2条SQL,都是INSERT

在一对多的情况下保存用户和客户需要3条SQL语句,2条INSERT,1条UPDATE

如果双向一对多的情况下,可能会出现一下情况

问题代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package cn.itcast.dao;

import cn.itcast.domain.Customer2;
import cn.itcast.domain.LinkMan2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ManyToOne2 {
@Autowired
private CustomerDao2 customerDao2 ;
@Autowired
private LinkManDao2 linkManDao2;

@Test
@Transactional
@Rollback(false)
public void addTest(){
LinkMan2 linkMan = new LinkMan2();
linkMan.setLkmEmail("郝美丽@foxmail.com");
linkMan.setLkmGender("女");
linkMan.setLkmMobile("10086");
linkMan.setLkmName("郝美丽");

Customer2 customer = new Customer2();
customer.setCustName("客户");

//通过联系人维护关系
linkMan.setCustomer2(customer);
//通过客户维护关系
customer.getLinkMan2s().add(linkMan);

customerDao2.save(customer);
linkManDao2.save(linkMan);

}
}

执行结果

问题

上面代码两个实体类都在维护关系,如果由联系人维护只需要执行2条SQL,如果有客户联系只需要执行3条SQL。运行后发现执行了3条SQL,说明是有客户实体类维护关系的,如果可以由联系人实体类维护关系岂不是更好。

解决办法

其中一个实体类放弃关系的维护,最好客户实体类放弃,因为联系人维护关系可以少执行一条SQL语句

修改客户实体类的OneToMany注解,并删除@JoinColumn注解,代码如下

1
2
3
//mappedBy 关系由另一个实体类维护,值是另一个实体类中属性的名字
@OneToMany(mappedBy = "customer2")
private Set<LinkMan2> linkMan2s = new HashSet<>();

级联操作

概念

操作一个实体类对象的同时会操作另一个实体类。

注意事项

1.级联操作一定明确操作主体

2.明确了操作主体后,需要在操作主体上指明允许级联操作

​ 操作方式:在@OnetToMany、@ManyToOne、@ManyToMany注解中添加属性cascade = CascadeType.ALL

​ cascade 属性说明

​ 定义源实体和关联的目标实体间的级联关系。当对源实体进行操作时,是否对关联的目标实体也做相同的操作。默认没有级联操作。该参数的可选值有:CascadeType.PERSIST(级联新建)CascadeType.REMOVE(级联删除)CascadeType.REFRESH(级联刷新)CascadeType.MERGE(级联更新)CascadeType.ALL(包含以上四项)

级联保存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Test
@Transactional
@Rollback(false)
/**
* 保存一的一方的同时多的一方会自动被添加到数据库
*/
public void add(){
LinkMan2 lm1 = new LinkMan2();
lm1.setLkmEmail("kefuA@foxmail.com");
lm1.setLkmGender("男");
lm1.setLkmMobile("110");
lm1.setLkmName("客服A");

LinkMan2 lm2 = new LinkMan2();
lm2.setLkmEmail("kefuB@foxmail.com");
lm2.setLkmGender("女");
lm2.setLkmMobile("119");
lm2.setLkmName("客服B");

Customer2 customer2 = new Customer2();
customer2.setCustName("JACK");

/*
用于关联联系人的,关联后在保存客户的同时就会顺便保存联系人
*/
customer2.getLinkMan2s().add(lm1);
customer2.getLinkMan2s().add(lm2);

/*
关联外键的
*/
lm1.setCustomer2(customer2);
lm2.setCustomer2(customer2);

//这里直接保存客户实体类就可以,联系人实体类会被自动保存
customer2Dao.save(customer2);
}

级联删除

1
2
3
4
5
6
7
8
9
@Test
@Transactional
@Rollback(false)
public void delete(){
Customer2 c = customerDao2.findOne(13L);
//与c用户相关联的联系人也会被删除
customerDao2.delete(c);

}

多对多

需求

一个用户(User)对应多个角色(Role)

一个角色(Role)可以对应多个用户(User)

创建表

这里不手动创建了,直接由框架自己生成表

编写实体类及映射关系

用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package cn.itcast.domain;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@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;



public Long getRoleId() {
return roleId;
}

public void setRoleId(Long roleId) {
this.roleId = roleId;
}

public String getRoleName() {
return roleName;
}

public void setRoleName(String roleName) {
this.roleName = roleName;
}


}

角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package cn.itcast.domain;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@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;



public Long getUserId() {
return userId;
}

public void setUserId(Long userId) {
this.userId = userId;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

}

多对多关系配置

1.给用户实体类(User)添加属性及映射关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
//JoinTable会生成中间表,表名叫做sys_user_role
@JoinTable(
name = "sys_user_role",
/*
joinColumns,配置外键(这里是配置中间表与当前用户表的关系)
name:副表(中间表)中的外键的名字
referencedColumnName主表(用户表)的主键
*/
joinColumns = {
@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")
},
/*
inverseJoinColumns,配置另一个表的外键(这里是配置中间与角色表的关系)
name:副表(中间表)中的外键的名字
referencedColumnName主表(用户表)的主键
*/
inverseJoinColumns = {
@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")
}
)
private Set<Role> roles = new HashSet<>();
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}

2.角色(Role)实体类添加属性及映射关系

关系由用户维护就好了,所以在角色实体类中用mapppedBy

1
2
3
4
5
6
7
8
9
10
//配置多对多
@ManyToMany(mappedBy = "roles") //配置多表关系
private Set<User> users = new HashSet<>();
public Set<User> getUsers() {
return users;
}

public void setUsers(Set<User> users) {
this.users = users;
}

编写接口

UserDao

1
2
3
4
5
6
7
8
package cn.itcast.dao;

import cn.itcast.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface UserDao extends JpaRepository<User,Long>,JpaSpecificationExecutor<User> {
}

RoleDao

1
2
3
4
5
6
7
8
9
package cn.itcast.dao;

import cn.itcast.domain.Role;
import cn.itcast.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface RoleDao extends JpaRepository<Role,Long> ,JpaSpecificationExecutor<Role> {
}

级联保存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package cn.itcast.dao;

import cn.itcast.domain.Role;
import cn.itcast.domain.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Many2ManyTest {
@Autowired
private UserDao userDao;
@Autowired
private RoleDao roleDao;

@Test
public void addTest(){
Role role = new Role();
role.setRoleName("管理员");

User user = new User();
user.setAge(13);
user.setUserName("老王");

//配置关系
user.getRoles().add(role);

userDao.save(user);

}
}

运行结果

级联删除

1
2
3
4
5
6
@Test
public void deleteTest(){
User user = userDao.findOne(1L);
userDao.delete(user);

}

运行结果

级联查询(对象导航查询)

1
2
3
4
5
6
7
8
@Test
public void findTest(){
User user = userDao.findOne(2L);
Set<Role> roles = user.getRoles();
for (Role role : roles) {
System.out.println(role.getRoleName());
}
}

报错

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role:

解决方案1:在User实体类的@ManyToMany注解中添加值fetch

1
@ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL,fetch = FetchType.EAGER)

解决方案2:在junit测试方法findTest()上加@Transactional

一对多关系对象查询总结

通过一的一方查询多的一方默认是延迟查询,因为多的一方可能数据非常多,浪费时间,浪费内存

通过多的一方查询一的一方默认是立即查询,因为一的一方就一条数据,性能可以忽略不计

推荐:使用默认设置,如果使用默认设置,在进行对象查询的时候可能会报org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role:,原因是事务关闭了,我们需要加@Transactional

文章作者: 微信:hao_yongliang
文章链接: https://haoyongliang.gitee.io/2019/07/13/SpringBoot/SpringBoot2.X%E9%9B%86%E6%88%90SpringDataJPA(%E4%B8%89)/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 郝永亮的主页
打赏
  • 微信
    微信
  • 支付寶
    支付寶

评论