SpringDataJpa (二)-动态查询&多表操作
一.动态查询1.Specifications动态查询1.1 搭建测试环境1.1.1 导入坐标1.1.2 创建客户实体类1.1.3 springdatajpa 核心配置文件1.1.4 编写dao接口1.1.5 案列测试代码1.1.5.1 查询所有1.1.5.2 多条件查询1.1.5.3 模糊查询1.1.5.4 Sort排序1.1.5.5 pageable分页
2.多表查询(一对多)2.1.配置环境2.1.1 新建一个工程,添加两个表的类并配置映射关系2.1.2 测试保存操作
2.2 级联2.2.1 级联添加2.2.2 级联删除
3.多对多3.1 配置环境3.2 测试保存代码3.3 多对多放弃维护权3.4 多对多级联操作
4. 对象导航查询
一.动态查询
1.Specifications动态查询
继承的是JpaSpecificationExecutor接口 方法列表: 1.findone(Specification spec) 2.List findAll(Specification spec) Pageable 查询全部分页 3.Page findAll(Specification spec,Pageable pageable) Sort 排序 4…List findAll(Specification spec,Sort sort) 5.long count(Specification spec)
Specification(是一个接口):查询条件 需要自定义我们自己的Specification实现类 需要实现以下方法: Predicate toPredicate (Root root,CriteriaQuery<?> query,CriteriaBuilder cb) 其中的参数: 1.root:查询的根对象(查询的任何属性都可以从跟根对象中获取) 2.CriteriaQuery:顶层查询对象,自定义查询方法(了解,一般不用) 3.CriteriaBuilder:查询的构造器,封装很多查询条件
1.1 搭建测试环境
1.1.1 导入坐标
<properties>
<spring.version>4.2.4.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.9</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
>
<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
>
<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>4.2.4.RELEASE
</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
>
<dependency>
<groupId>javax
.xml
.bind
</groupId
>
<artifactId>jaxb
-api
</artifactId
>
<version>2.3.0</version
>
</dependency
>
<dependency>
<groupId>com
.sun
.xml
.bind
</groupId
>
<artifactId>jaxb
-impl
</artifactId
>
<version>2.3.0</version
>
</dependency
>
<dependency>
<groupId>com
.sun
.xml
.bind
</groupId
>
<artifactId>jaxb
-core
</artifactId
>
<version>2.3.0</version
>
</dependency
>
<dependency>
<groupId>javax
.activation
</groupId
>
<artifactId>activation
</artifactId
>
<version>1.1.1</version
>
</dependency
>
<!-- el end
-->
</dependencies
>
<build>
<pluginManagement>
<plugins>
<!--jdk编译插件
-->
<plugin>
<groupId>org
.apache
.maven
.plugins
</groupId
>
<artifactId>maven
-compiler
-plugin
</artifactId
>
<version>3.8.0</version
>
<configuration>
<source>1.8</source
>
<target>1.8</target
>
<encoding>utf
-8</encoding
>
</configuration
>
</plugin
>
<!--tomcat插件
-->
<plugin>
<groupId>org
.apache
.tomcat
.maven
</groupId
>
<!-- tomcat7的插件, 不同tomcat版本这个也不一样
-->
<artifactId>tomcat7
-maven
-plugin
</artifactId
>
<version>2.1</version
>
<configuration>
<!-- 通过maven tomcat7
:run运行项目时,访问项目的端口号
-->
<port>80</port
>
<!-- 项目访问路径 本例:localhost
:9090, 如果配置的aa, 则访问路径为localhost
:9090/aa
-->
<path>/travel
</path
>
</configuration
>
</plugin
>
</plugins
>
</pluginManagement
>
</build
>
1.1.2 创建客户实体类
@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_industry")
private String custIndustry
;
@Column(name
= "cust_level")
private String custLevel
;
@Column(name
= "cust_address")
private String custAddress
;
@Column(name
= "cust_phone")
private String custPhone
;
1.1.3 springdatajpa 核心配置文件
<?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"
>
<bean id
="dataSources" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name
="driverClass" value
="com.mysql.jdbc.Driver" />
<property name
="jdbcUrl" value
="jdbc:mysql:///jpa" />
<property name
="user" value
="root" />
<property name
="password" value
="root" />
</bean
>
<!--1.创建entityManagerfactory对象交给spring容器管理
-->
<bean id
="entityManager" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name
="dataSource" ref
="dataSources"></property
>
<!-- 2.配置的是扫描的包,实体类所在包
-->
<property name
="packagesToScan" value
="cn.xkm.domain"></property
>
<!-- 3.jpa实现厂家
-->
<property name
="persistenceProvider">
<bean
class="org.hibernate.jpa.HibernatePersistenceProvider"></bean
>
</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" />
<property name
="showSql" value
="true" />
</bean
>
</property
>
<!-- jpa的方言:高级的特性
-->
<property name
="jpaDialect">
<bean
class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean
>
</property
>
</bean
>
<!-- 整合springdatajpa
-->
<jpa
:repositories base
-package="cn.xkm.dao" transaction
-manager
-ref
="transactionManager"
entity
-manager
-factory
-ref
="entityManager"></jpa
:repositories
>
<bean id
="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name
="entityManagerFactory" ref
="entityManager"></property
>
</bean
>
<!-- spring声明式事务
,此处省略
-->
<!-- 注解包扫描
-->
<context
:component
-scan base
-package="cn.xkm"></context
:component
-scan
>
</beans
>
1.1.4 编写dao接口
public interface Customer extends JpaRepository<cn
.xkm
.domain
.Customer
,Long
>, JpaSpecificationExecutor
<cn
.xkm
.domain
.Customer
> {
}
1.1.5 案列测试代码
1.1.5.1 查询所有
@RunWith(SpringJUnit4ClassRunner
.class)
@ContextConfiguration(locations
= "classpath:applicationContext.xml")
public class SpecTest {
@Autowired
private CustomerDao customerDao
;
@Test
public void testSpec(){
Specification
<Customer> spec
= new Specification<Customer>(){
@Override
public Predicate
toPredicate(Root
<Customer> root
, CriteriaQuery
<?> criteriaQuery
, CriteriaBuilder criteriaBuilder
) {
Path
<Object> custName
= root
.get("custName");
Predicate xkm
= criteriaBuilder
.equal(custName
, "xkm");
return xkm
;
}
};
List
<Customer> all
= customerDao
.findAll(spec
);
all
.forEach(a
-> System
.out
.println(a
));
}
}
1.1.5.2 多条件查询
@RunWith(SpringJUnit4ClassRunner
.class)
@ContextConfiguration(locations
= "classpath:applicationContext.xml")
public class SpecTest {
@Autowired
private CustomerDao customerDao
;
@Test
public void testSpec(){
Specification
<Customer> spec
= new Specification<Customer>(){
@Override
public Predicate
toPredicate(Root
<Customer> root
, CriteriaQuery
<?> criteriaQuery
, CriteriaBuilder criteriaBuilder
) {
Path
<Object> custName
= root
.get("custName");
Path
<Object> custIndustry
= root
.get("custIndustry");
Predicate xkm
= criteriaBuilder
.equal(custName
, "xkm");
Predicate boss
= criteriaBuilder
.equal(custIndustry
, "Boss");
Predicate and
= criteriaBuilder
.and(xkm
, boss
);
return and
;
}
};
Customer one
= customerDao
.findOne(spec
);
System
.out
.println(one
);
}
}
1.1.5.3 模糊查询
@RunWith(SpringJUnit4ClassRunner
.class)
@ContextConfiguration(locations
= "classpath:applicationContext.xml")
public class SpecTest {
@Autowired
private CustomerDao customerDao
;
@Test
public void testSpec(){
Specification
<Customer> spec
= new Specification<Customer>(){
@Override
public Predicate
toPredicate(Root
<Customer> root
, CriteriaQuery
<?> criteriaQuery
, CriteriaBuilder criteriaBuilder
) {
Path
<Object> custName
= root
.get("custName");
Predicate like
= criteriaBuilder
.like(custName
.as(String
.class), "x%");
return like
;
}
};
List
<Customer> all
= customerDao
.findAll(spec
);
all
.forEach(a
-> System
.out
.println(all
));
}
1.1.5.4 Sort排序
Sort sort
= new Sort(Sort
.Direction
.DESC
,"custId");
List
<Customer> all
= customerDao
.findAll(spec
,sort
);
for (Customer customer
: all
) {
System
.out
.println(customer
);
}
1.1.5.5 pageable分页
Pageable pageable
= new PageRequest(0, 2);
Page
<Customer> all
= customerDao
.findAll(spec
, pageable
);
for (Customer customer
: all
) {
System
.out
.println(customer
);
}
System
.out
.println(all
.getContent());
System
.out
.println(all
.getTotalPages());
System
.out
.println(all
.getTotalElements());
}
2.多表查询(一对多)
2.1.配置环境
2.1.1 新建一个工程,添加两个表的类并配置映射关系
@OneToMany(mappedBy
= "customer")
private Set
<LinkMan> linkmans
= new HashSet<>();
@ManyToOne(targetEntity
= Customer
.class,fetch
= FetchType
.LAZY
)
@JoinColumn(name
= "lkm_cust_id",referencedColumnName
= "cust_id")
private Customer customer
;
2.1.2 测试保存操作
@RunWith(SpringJUnit4ClassRunner
.class)
@ContextConfiguration(locations
= "classpath:applicationContext.xml")
public class SpecTest {
@Autowired
private CustomerDao customerDao
;
@Autowired
private LinkManDao linkManDao
;
@Test
@Transactional
@Rollback(false)
public void saveTest(){
Customer customer
= new Customer();
customer
.setCustName("向科铭");
LinkMan linkMan
= new LinkMan();
linkMan
.setLkmName("美国队长");
linkMan
.setCystomer(customer
);
customerDao
.save(customer
);
linkManDao
.save(linkMan
);
}
}
2.2 级联
概述: 1.级联添加:当我保存一个客户的同时保存联系人 2.级联删除:当我删除一个客户的同时删除联系人
级联操作: 1.需要区分操作主体 2.需要需要在操作主体的实体类上,添加级联属性(需要添加到多表映射关系的注解上) 3.cascade(配置级联)
2.2.1 级联添加
在多表中添加cascade = CascadeType.ALL属性,然后测试代码配置好关系保存客户即可
@OneToMany(mappedBy
= "customer" ,cascade
= CascadeType
.ALL
)
private Set
<LinkMan> linkmans
= new HashSet<>();
public void saveTest(){
Customer customer
= new Customer();
customer
.setCustName("向科铭");
LinkMan linkMan
= new LinkMan();
linkMan
.setLkmName("美国队长");
linkMan
.setCustomer(customer
);
customer
.getLinkmans().add(linkMan
);
customerDao
.save(customer
);
2.2.2 级联删除
配置好以上关系后,查询出来删除
public void saveTest(){
Customer one
= customerDao
.findOne(1l
);
customerDao
.delete(one
);
}
3.多对多
3.1 配置环境
两个表,分别配置好关系
@ManyToMany(targetEntity
= User
.class)
@JoinTable(name
= "sys_userandrole",
joinColumns
={@JoinColumn(name
= "sys_roleid",referencedColumnName
= "role_id")},
inverseJoinColumns
= {@JoinColumn(name
= "sys_userid",referencedColumnName
= "user_id")})
private Set
<User> users
= new HashSet<>();
@ManyToMany(targetEntity
= Role
.class)
@JoinTable(name
= "sys_userandrole",
joinColumns
={@JoinColumn(name
= "sys_userid",referencedColumnName
= "user_id")},
inverseJoinColumns
= {@JoinColumn(name
= "sys_roleid",referencedColumnName
= "role_id")})
private Set
<Role> roles
= new HashSet<>();
3.2 测试保存代码
@Test
@Transactional
@Rollback(false)
public void saveTest(){
User user
= new User();
Role role
= new Role();
user
.setUserName("向科铭");
role
.setRoleName("老板");
user
.getRoles().add(role
);
userDao
.save(user
);
roleDao
.save(role
);
}
3.3 多对多放弃维护权
原则:谁被动被选择谁放弃 删除中间表的配置,再配置自己再对方的映射set集合名称
@ManyToMany(mappedBy
= "roles")
private Set
<User> users
= new HashSet<>();
3.4 多对多级联操作
原则:确认主体,再主体的类上加上级联操作cascade = CascadeType.ALL
@ManyToMany(targetEntity
= Role
.class,cascade
= CascadeType
.ALL
)
@JoinTable(name
= "sys_userandrole",
joinColumns
={@JoinColumn(name
= "sys_userid",referencedColumnName
= "user_id")},
inverseJoinColumns
= {@JoinColumn(name
= "sys_roleid",referencedColumnName
= "role_id")})
private Set
<Role> roles
= new HashSet<>();
级联删除
public void saveTest(){
User one
= userDao
.findOne(1l
);
userDao
.delete(one
);
}
4. 对象导航查询
概述:通过一个对象通过get方法将关联对象也查询出来, (一对多,只需要查询出1的内容,可以getLinkmeans()得到对应的多内容,默认是延迟加载) (多对一,只要查出多的内容再get一的方法即可,默认是立即加载)
@Test
@Transactional
@Rollback(false)
public void saveTest(){
Customer one
= customerDao
.getOne(1l
);
Set
<LinkMan> linkmans
= one
.getLinkmans();
for (LinkMan linkman
: linkmans
) {
System
.out
.println(linkman
);
}
}
注:对象导航查询 一查多默认使用的是延迟加载的形式查询的 调用get方法不会立即发送查询,而是在使用关联对象的时候才会查询。
修改配置:添加以下配置开启立即加载(不推荐使用) fetch = FetchType.EAGER (FetchType.LAZY 延迟加载)
@OneToMany(mappedBy
= "customer" ,cascade
= CascadeType
.ALL
,fetch
= FetchType
.EAGER
)
private Set
<LinkMan> linkmans
= new HashSet<>();