SpringDataJPA
一丶JPA快速起步
数据库表SQL
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 AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
1. IDEA新建项目
普通maven项目
2.导入依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.hibernate.version>5.0.7.Final</project.hibernate.version>
</properties>
<dependencies>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- hibernate对jpa的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- log日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- Mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
3.配置文件
在java工程的src路径下创建一个名为META-INF的文件夹,在此文件夹下创建一个名为persistence.xml的配置文件
模板可以在IDEA上直接找到-步骤如下:
刚出来文件报错,是因为没有必要的配置:,完事儿了就不报错了
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!--需要配置 persistence-unit节点 -->
<!--配置持久化单元
name:持久化单元名称
transaction-type:事务类型
RESOURCE_LOCAL:本地事务管理
JTA:分布式事务管理 -->
<persistence-unit name="MyJpa" transaction-type="RESOURCE_LOCAL">
<!-- jpa的实现方式 本次是Hibernate-->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 可选配置,配置jpa实现方的配置信息 -->
<properties>
<!-- 数据库信息
用户名:javax.persistence.jdbc.user
密码:javax.persistence.jdbc.password
驱动:javax.persistence.jdbc.driver
地址:javax.persistence.jdbc.url
-->
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="root"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF8"/>
<!-- 配置jpa实现方(hibernate)的实现信息
显示SQL hibernate.show_sql->false:true
自动创建数据库表 hibernate.hbm2ddl.auto->
create程序运行时创建数据库表(如果有表,先删除表再创建)
update程序运行时创建表(如果有表,不会创建表)
none不会创建表
-->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>
</persistence>
4.实体类
import javax.persistence.*;
/**
* @Author: Valentino
* @QQ: 3289668817
* @Email:gentao.xiong
* @CreateTime: 2020-09-08 15:24:20
* @Descirption:
*/
/**
* 客户实体类
* 配置映射关系:
* 1.实体类和表的映射关系
* @Entity 声明实体类
* @Table 配置实体类和表的映射关系
* name : 配置数据库表的名称
* 2.实体类中属性和表中字段的映射关系
* @ID 声明主键的配置
* @GeneratedValue(generator = GenerationType.IDENTITY) 生成策略,自增
* @Column(name = "cust_id") 数据库表中字段的名称
*
*/
@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;
@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custLevel='" + custLevel + '\'' +
", custAddress='" + custAddress + '\'' +
", custPhone='" + custPhone + '\'' +
'}';
}
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 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 getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
public Customer() {
}
public Customer(Long custId, String custName, String custSource, String custIndustry, String custLevel, String custAddress, String custPhone) {
this.custId = custId;
this.custName = custName;
this.custSource = custSource;
this.custIndustry = custIndustry;
this.custLevel = custLevel;
this.custAddress = custAddress;
this.custPhone = custPhone;
}
}
5.新增测试
import org.junit.Test;
import per.xgt.pojo.Customer;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
/**
* @Author: Valentino
* @QQ: 3289668817
* @Email:gentao.xiong
* @CreateTime: 2020-09-08 15:39:08
* @Descirption:
*/
public class Test1 {
/**
* 测试jpa的保存:保存一个客户到数据库中
*
* jpa的操作步骤
* 1.加载配置文件创建工程(尸体管理类工厂)对象
* Persistence 静态方法(根据持久化单元名称创建实体管理类管理工厂)
* createEntityManagerFactory(持久化单元命长)
* 作用:创建实体管理工厂
* 2.通过实体管理类工厂获取实体管理器
* EntityManagerFactory:获取EntityManager对象
* 方法:createEntityManager
* ----内部维护了数据库信息、缓存信息、所有的实体管理器对象、根据配置创建数据库表
* ----EntityManagerFactory的创建过程比较浪费资源
* ----特点:线程安全->多个线程访问同一个EntityManagerFactory不会有线程安全问题
* 3.获取事务对象开启事务
* EntityManager对象:实体类管理器
* beginTransaction : 创建事务对象
* persist : 保存
* merge : 更新
* remove : 删除
* find/getRefrence : 根据id查询
* Transaction对象 : 事务
* begin : 开启
* commit : 提交
* rollback : 回滚
* 4.完成数据库操作
* 5.提交事务或回滚事务
* 6.释放资源
*
*/
@Test
public void testSave(){
//加载配置文件创建工程(尸体管理类工厂)对象
EntityManagerFactory factory = Persistence.createEntityManagerFactory("MyJpa");
//通过实体管理类工厂获取实体管理器
EntityManager em = factory.createEntityManager();
//获取事务对象开启事务
//1.获取事务对象
EntityTransaction tx = em.getTransaction();
//2.开启事务
tx.begin();
//完成数据库操作--保存一个客户
Customer customer = new Customer();
customer.setCustName("大桥");
customer.setCustIndustry("演员");
//保存操作
em.persist(customer);
//提交事务或回滚事务
tx.commit();
//释放资源
em.close();
factory.close();
}
}
备注
hibernate创建数据库表说明
无论执行多少次,数据库都只有一条最后插入的数据:原因如下:
所以我们因该改成update
如果改成了none,就会报table不存在的错误,改成其他两个,就会在数据库创建表。
主键生成策略
@GeneratedValue(strategy = GenerationType.IDENTITY) 生成策略,自增(数据库必须支持自增)
@GeneratedValue(strategy = GenerationType.SEQUENCE) 生成策略,序列(数据库必须支持序列)
@GeneratedValue(strategy = GenerationType.TABLE) jpa提供的一种机制,通过一张数据库表的形式帮助完成主键自增
@GeneratedValue(strategy = GenerationType.AUTO) 由程序自动的帮助我们选择主键生成策略,根据环境配置和数据库自动选择
建议使用MYSQL或者Oracle的时候选择前两种的一种
如何解决创建EntityManagerFactory浪费资源、时间的问题?
思路:创建公共EntityManagerFactory对象
在java环境下没在web环境下 可以使用静态代码块的形式创建EntityManagerFactory
public class JpaUtil {
private static EntityManagerFactory factory;
static {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("MyJpa");
}
/**
* 获取管理实体对象
*/
public static EntityManager getEntityManager(){
return factory.createEntityManager();
}
}
6.查询测试
@Test
public void testFind(){
//1.通过工具类获取对象
EntityManager manager = JpaUtil.getEntityManager();
//2.开启事务
EntityTransaction tx = manager.getTransaction();
tx.begin();
//3.数据库操作
/**
* find : 根据id查询数据
* class : 查询数据的结果需要包装的实体类的字节码
* id : 主键取值
*/
Customer customer = manager.find(Customer.class, 2l);
System.out.println(customer);
//4.提交事务
tx.commit();
//5.释放资源
manager.close();
}
7.延迟加载|立即加载
使用find方法查询:立即加载
查询的对象就是当前客户对象本身
在调用find方法的时候,就会发送sql语句查询数据库
使用getReference方法:延迟加载(懒加载)
获取的对象是一个动态代理对象
调用getReference方法不会立即发送sql语句查询数据库,当调用查询结果对象的时候,才会发送查询的sql语句,什么时候用,什么时候发送sql数据执行查询
8.删除测试
@Test
public void testRemove(){
//1.通过工具类获取对象
EntityManager manager = JpaUtil.getEntityManager();
//2.开启事务
EntityTransaction tx = manager.getTransaction();
tx.begin();
//3.数据库操作
/**
* 参数是一个对象:
* 1.根据id查询客户
* 2.调用remove方法完成删除操作
*/
Customer customer = manager.find(Customer.class, 2l);
manager.remove(customer);
//4.提交事务
tx.commit();
//5.释放资源
manager.close();
}
9.更新测试
@Test
public void testUpdate(){
//1.通过工具类获取对象
EntityManager manager = JpaUtil.getEntityManager();
//2.开启事务
EntityTransaction tx = manager.getTransaction();
tx.begin();
//3.数据库操作
/**
* 参数是一个对象:
* 1.查询客户
* 2.调用merge方法完成更新操作
*/
Customer customer = manager.find(Customer.class, 1l);
customer.setCustAddress("日本");
manager.merge(customer);
//4.提交事务
tx.commit();
//5.释放资源
manager.close();
}
二丶JPQL负责查询
JPQL(java Persistence Query Language) 是JPA 提供的查询语句
完全面向对象,通过类名和属性访问,而不是表名和表的属性
1.查询全部
@Test
public void testFindAll(){
//1.通过工具类获取EntityManager对象
EntityManager em = JpaUtil.getEntityManager();
//2.开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.数据库操作
String jpql = "from per.xgt.pojo.Customer";
//创建query查询对象,这才是执行jpql的对象
Query query = em.createQuery(jpql);
//发送查询并封装结果集
List list = query.getResultList();
for (Object obj : list){
System.out.println(obj);
}
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
from 后面可以省略包名
2.分页查询
@Test
public void testPage(){
//1.通过工具类获取EntityManager对象
EntityManager em = JpaUtil.getEntityManager();
//2.开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.数据库操作
String jpql = "from Customer";
//创建query查询对象,这才是执行jpql的对象
Query query = em.createQuery(jpql);
//起始索引
query.setFirstResult(0);
//每页查询条数
query.setMaxResults(2);
//发送查询并封装结果集
List list = query.getResultList();
for (Object obj : list){
System.out.println(obj);
}
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
3.统计查询
@Test
public void testCount(){
//1.通过工具类获取EntityManager对象
EntityManager em = JpaUtil.getEntityManager();
//2.开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.数据库操作
String jpql = "select count(custId) from Customer";
//创建query查询对象,这才是执行jpql的对象
Query query = em.createQuery(jpql);
//发送查询并封装结果集
/**
* getResultList:直接将查询结果封装为list集合
* getSingleResult:得到唯一的结果
*/
Object result = query.getSingleResult();
System.out.println(result);
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
备注
getResultList:直接将查询结果封装为list集合
getSingleResult:得到唯一的结果
4.条件查询
/**
* 条件查询
* 客户名称以大开头的客户
*/
@Test
public void testCondition(){
//1.通过工具类获取EntityManager对象
EntityManager em = JpaUtil.getEntityManager();
//2.开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.数据库操作
String jpql = "from Customer where custName like ?";
//创建query查询对象,这才是执行jpql的对象
Query query = em.createQuery(jpql);
//给占位符参数赋值(第一个参数 是占位符索引位置,第二个参数 占位符取值)
query.setParameter(1, "大%");
//发送查询并封装结果集
List list = query.getResultList();
for (Object obj : list){
System.out.println(obj);
}
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
5.排序查询
@Test
public void testOrder(){
//1.通过工具类获取EntityManager对象
EntityManager em = JpaUtil.getEntityManager();
//2.开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.数据库操作
String jpql = "from Customer order by custId desc ";
//创建query查询对象,这才是执行jpql的对象
Query query = em.createQuery(jpql);
//发送查询并封装结果集
List list = query.getResultList();
for (Object obj : list){
System.out.println(obj);
}
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
三丶三者之间的关系
SpringDataJpa、jpa、hibernate之间的联系:
JPA由接口和抽象类组成,Hibernate是一套ORM框架,实现了JPA规范。SpringDataJPA是Spring对JPA操作的封装,是在JPA规范下进行数据持久化的解决方案。
四丶SpringDataJPA快速起步
新建一个maven工程:同上一样的
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 AOP相关坐标 -->
<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>
<!-- spring IOC相关坐标 -->
<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>
<!-- hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- Hibernate对JPA实现的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- Hibernate数据验证包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.1.Final</version>
</dependency>
<!-- c3p0连接池 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<!-- log -->
<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>
<!-- mysql数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- SpringDataJPA的坐标 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.9.0.RELEASE</version>
</dependency>
<!-- Spring对junit支持坐标 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- 使用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>
</dependencies>
2.配置文件
放在Resources目录下就行了
<?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">
<!-- Spring和SpringDataJPA的配置 -->
<!-- 1.创建EntityManagerFactory对象交给Spring容器管理 -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 配置的扫描的包:实体类所在的包 -->
<property name="packagesToScan" value="per.xgt.pojo" />
<!-- jpa的实现方式 ->Hibernate -->
<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>
</bean>
<!-- 2.创建数据库连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="password" value="root" />
<property name="user" value="root" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF8" />
<property name="driverClass" value="com.mysql.jdbc.Driver" />
</bean>
<!-- 3.整合SPringDataJPA -->
<jpa:repositories base-package="per.xgt.dao" transaction-manager-ref="transactionManager" entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
<!-- 4.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<!-- 5.声明式事务 -->
<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>
<!-- 5.aop -->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* per.xgt.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
</aop:config>
<!-- 6.配置包扫描 -->
<context:component-scan base-package="per.xgt" ></context:component-scan>
</beans>
实体类与上一样
3.编写JPA规范接口
/**
* 符合SpringDataJPA的dao层接口规范
* JpaRepository : 操作的实体类类型,实体类中主键属性的类型
* --封装了基本的CRUD操作
* JpaSpecificationExecutor : 操作的实体类类型
* --封装了复杂查询的操作
*/
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
}
所有的方法都已经集成在了继承的两个接口中,所以不需要写实现类
4.测试查询
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;
import per.xgt.dao.CustomerDao;
import per.xgt.pojo.Customer;
/**
* @Author: Valentino
* @QQ: 3289668817
* @Email:gentao.xiong
* @CreateTime: 2020-09-09 15:44:38
* @Descirption:
*/
@RunWith(SpringJUnit4ClassRunner.class)//声明Spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//指定Spring容器的配置信息
public class CustomerTest {
@Autowired
private CustomerDao customerDao;
@Test
public void testFindOne(){
Customer customer = customerDao.findOne(1l);
System.out.println(customer);
}
}
方法都已经定义在了dao层接口中继承的接口中。
5.添加和修改
/**
* save保存或更新
* 根据传递的对象是否存在主键ID
* --如果没有,保存
* --如果存在,根据ID查询,然后更新数据
*/
5.1.添加
@Test
public void testSave(){
Customer customer = new Customer();
customer.setCustName("佐伯");
customer.setCustLevel("骑兵");
customer.setCustIndustry("演员");
customerDao.save(customer);
}
5.2.修改
@Test
public void testUpdate(){
Customer customer = new Customer();
customer.setCustId(2l);
customer.setCustName("结衣");
customer.setCustLevel("步兵");
customer.setCustIndustry("演员");
customerDao.save(customer);
}
本来有值的address字段,变成了null,所以在执行查询的时候,应该先执行查询,然后再用set方法更新类属性再调用sava方法执行数据库操作。
6.删除
@Test
public void testDelete(){
customerDao.delete(4l);
}
先执行的查询,再执行的删除,所以是先看有不有,有再删除
7.查询所有
@Test
public void testFindAll(){
List<Customer> list = customerDao.findAll();
for (Customer customer : list) {
System.out.println(customer);
}
}
8.调用接口查询
8.1.统计查询
count方法用来统计总条数的
@Test
public void testCount(){
long count = customerDao.count();//查询全部的客户数量
System.out.println(count);
}
8.2存在查询
@Test
public void testExists(){
boolean exists = customerDao.exists(1l);
System.out.println(exists);
}
注意SQL,就知道原理了
9.findOne个getOne
9.1.getOne
/**
* @Transactional 保证getOne正常运行
*/
@Test
@Transactional
public void testGetOne(){
Customer exists = customerDao.getOne(1l);
System.out.println(exists);
}
区别
getOne调用的是getReference方法:延迟加载
findOne调用的是em.find方法:立即加载
10.JPQL查询
语法或关键字与sql相似,查询的是类和类中的属性
使用:
需要将jpsql语句配置到接口方法上:
1.将有的查询:需要再dao接口上配置方法
2.再新添加的方法上,使用注解的形式配置jpsql查询语句
3.注解 : @Query
10.1.根据名字查询
在dao层,自定义方法,然后使用注解@Query
//根据客户名称查询客户->使用jpql查询
@Query(value = "from Customer where custName = ?")
public Customer findJPQLByCustName(String custName);
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class JPQLTest {
@Autowired
private CustomerDao customerDao;
@Test
public void testFindJPQLByCustName(){
Customer customer = customerDao.findJPQLByCustName("大桥");
System.out.println(customer);
}
}
结果
10.2.多占位符赋值
根据id和名字查询custmer
//根据客户名称和客户ID查询
@Query(value = "from Customer where custName = ? and custId = ?")
public Customer findJPQLByCustNameAndCustId(String name,Long id);
@Test
public void findJPQLByCustNameAndCustId(){
Customer customer = customerDao.findJPQLByCustNameAndCustId("大桥", 1l);
System.out.println(customer);
}
备注
默认情况下:占位符的位置需要和方法参数中的位置保持一致
但是也可以指定占位符参数的位置
? 索引 :指定占位符的取值来源,如下
//根据客户名称和客户ID查询
@Query(value = "from Customer where custName = ?2 and custId = ?1")
public Customer findJPQLByCustNameAndCustId(Long id,String name);
10.3.更新
//更新操作->根据id更新客户
@Query(value = "update Customer set custAddress = ?2 where custId = ?1")
@Modifying //声明此方法是用来进行更新操作的
public void updateCustAddressByCustId(long id,String address);
注意
SpringDataJPA中使用jpql完成更新操作
1.需要手动添加事务的支持->@Transactional
2.默认会回滚事务->
@Rollback : 设置是否回滚,true:false
/**
* SpringDataJPA中使用jpql完成更新/删除操作
* 1.需要手动添加事务的支持->@Transactional
* 2.默认会回滚事务->
* @Rollback : 设置是否回滚,true:false
*/
@Test
@Transactional //添加事务注解
@Rollback(value = false)
public void updateCustAddressByCustId(){
customerDao.updateCustAddressByCustId(2l, "日本");
}
11.SQL查询
11.1.查询全部
value:sql语句
nativeQuery: true:false 查询方式是否是本地sql查询
返回结果集 是 List<Object[]> 固定写法
/**
* value:sql语句
* nativeQuery: true:false 查询方式是否是sql查询
* @return
*/
//使用SQL查询全部
@Query(value = "select * from cst_customer",nativeQuery = true)
public List<Object[]> findAllBySQL();
@Test
public void testfindAllBySQL(){
List<Object[]> list = customerDao.findAllBySQL();
for (Object[] obj : list) {
System.out.println(Arrays.toString(obj));
}
}
效果
11.2. 带参数查询
//使用SQL根据名字模糊匹配查询
@Query(value = "select * from cst_customer where cust_name like ?1",nativeQuery = true)
public List<Object[]> findAllBySQLOFCustName(String name);
@Test
public void testfindAllBySQLOFCustName(){
List<Object[]> list = customerDao.findAllBySQLOFCustName("大%");
for (Object[] obj : list) {
System.out.println(Arrays.toString(obj));
}
}
12.方法命名查询
12.1.查询全部
/**
* 接口方法命名查询:
* 约定:findBy:查询
* 对象属性名称(首字母大写)->查询条件
* 在SpringDataJPA运行阶段会根据方法名称进行解析
*/
//根据客户名称查询
public Customer findByCustName(String custName);
@Test
public void testfindByCustName(){
Customer customer = customerDao.findByCustName("大桥");
System.out.println(customer);
}
运行得时候,会根据方法名进行解析,解析成SQL语句
12.2.模糊查询
/**
* findBy + 属性名称 ->根据属性名称进行匹配查询
* findBy + 属性名称 + 查询方式(like、isnull...) ->根据指定查询方式进行查询
*/
public List<Customer> findByCustNameLike(String name);
@Test
public void testfindByCustName(){
List<Customer> customers = customerDao.findByCustNameLike("大%");
for (Customer customer : customers) {
System.out.println(customer);
}
}
12.3.多条件查询
/**
* findBy + 属性名 + 查询方式 + 多条件连接符(and、or...) + 属性名 + 查询方式......
* 注意:参数要与方法条件查询顺序一致
*/
//使用客户名称模糊匹配和客户所属行业精准匹配
public List<Customer> findByCustNameLikeAndCustIndustry(String name,String industry);
@Test
public void testfindByCustNameLikeAndCustIndustry(){
List<Customer> customers = customerDao.findByCustNameLikeAndCustIndustry("大%", "演员");
for (Customer customer : customers) {
System.out.println(customer);
}
}
13.Specifications动态查询
//查询单个对象
T findOne(Specification<T> var1);
//查询列表
List<T> findAll(Specification<T> var1);
//查询全部-分页(查询条件,分页参数(分页对象,SpringDataJPA提供))
Page<T> findAll(Specification<T> var1, Pageable var2);
//查询全部(查询条件参数,排序参数)
List<T> findAll(Specification<T> var1, Sort var2);
//条件统计查询
long count(Specification<T> var1);
13.1.Specification
Specification查询条件:
自定义自己的Specification实现类:
实现toPredicate:
参数1:Root:查询的根对象(查询的任何属性都可以从跟对象中获取)
参数2:CriteriaQuery:顶层查询方式,自定义查询方式(一般不用)
参数3:CriteriaBuilder:查询的构造器,封装了许多查询条件
/**
* 匿名内部类->自定义查询条件
* 1.实现Specification接口(提供反省,查询的对象类型)
* 2.实现toPredicate方法(构造查询条件)
* 3.需要借助方法参数中的两个参数:
* 1)root:获取需要查询的对象属性
* 2)CriteriaBuilder:构造查询条件,内部封装了很多查询条件(模糊匹配,精准匹配)
*/
13.2.单个条件查询
@Test
public void testSpec(){
Specification<Customer> spec = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
//获取比较的属性-->get方法,参数为属性的名称
Path<Object> custName = root.get("custName");
//构造查询条件->equal进行精准匹配(比较的属性(Path对象),属性的取值)
Predicate predicate = criteriaBuilder.equal(custName, "大桥");
return predicate;
}
};
Customer customer = customerDao.findOne(spec);
System.out.println(customer);
}
13.3.多条件查询
/**
* root:获取属性
* CriteriaBuilder:构造查询->
* 1.构造客户的精准匹配
* 2.构造所属行业的精准匹配
* 3.将两个条件联系起来
*/
/**
* 多条件拼接
* 根据客户名称和客户所属行业查询
*/
@Test
public void testSpecs(){
Specification<Customer> spec = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path custName = root.get("custName");
Path custIndustry = root.get("custIndustry");
Predicate p1 = criteriaBuilder.equal(custName, "大桥");
Predicate p2 = criteriaBuilder.equal(custIndustry, "演员");
//将多个条件组合到一起->and就是与->多个查询条件
Predicate predicate = criteriaBuilder.and(p1, p2);
return predicate;
}
};
Customer customer = customerDao.findOne(spec);
System.out.println(customer);
}
13.4.模糊匹配
/**
* equal:直接得到path对象(属性)就可以比较
* gt、lt、ge、le、like:不能直接用path对象比较,需要得到path对象,根据path指定比较的参数类型,再取进行比较
* 指定类型方式:path.as(类型的字节码对象)
*/
/**
* 根据客户名的模糊匹配,返回客户列表
*/
@Test
public void testSpec2(){
Specification<Customer> spec = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> custName = root.get("custName");
Predicate predicate = criteriaBuilder.like(custName.as(String.class), "大%");
return predicate;
}
};
List<Customer> customers = customerDao.findAll(spec);
for (Customer customer : customers) {
System.out.println(customer);
}
}
13.5.排序
//创建排序对象,需要调用构造方法实例化对象(排序的顺序,排序的属性)
@Test
public void testSpec2(){
Specification<Customer> spec = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> custName = root.get("custName");
Predicate predicate = criteriaBuilder.like(custName.as(String.class), "大%");
return predicate;
}
};
//创建排序对象,需要调用构造方法实例化对象(排序的顺序,排序的属性)
Sort sort = new Sort(Sort.Direction.DESC, "custId");
List<Customer> customers = customerDao.findAll(spec, sort);
for (Customer customer : customers) {
System.out.println(customer);
}
}
13.6.分页
/**
* 分页查询
* 参数(Specification查询条件,Pageable分页参数对象)
* PageRequest是Pagebble接口的实现类
* 返回:Page:SpringDataJPA封装好的pageBean对象,数据列表,共条数
*/
@Test
public void testSpec3(){
Specification spec = null;
// 参数(当前查询的页数->0开始,每页查询的数量)
PageRequest request = new PageRequest(0, 2);
Page page = customerDao.findAll(spec, request);
//getTotalElements 总条数
System.out.println(page.getTotalElements());
//getTotalPages 总页数
System.out.println(page.getTotalPages());
//getContent 结果列表
List content = page.getContent();
for (Object o : content) {
System.out.println(o);
}
}
14.多表操作
数据库SQL
/*创建客户表*/
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 AUTO_INCREMENT=94 DEFAULT CHARSET=utf8;
/*创建联系人表*/
CREATE TABLE cst_linkman (
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) NOT NULL COMMENT '客户id(外键)',
PRIMARY KEY (`lkm_id`),
KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
14.1.POJO类
/**
* 使用注解的形式配置多表关系
* 1.声明关系
* @OnetoMany : 字面意思->一对多
* targetEntiry : 对方对象的字节码对象
* 2.配置外键(中间表)
* @JoinColumn(name:外键字段名,referencedColumnName:参照的主表的主键字段名称)
*
* 在客户实体类上(一)添加了外键配置,所以客户具备了维护外键的作用
*/
/**
* 配置联系人到客户的多对一关系
*
* 使用注解配置多对一关系
* 1.配置表关系
* @ManyToOne(targetEntity = Customer.class)
* 2.配置外键()
* @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
*
* 配置外键的过程,配置到了多的一方,就会在多的一方维护外键
*/
@Entity
@Table(name = "cst_linkman")
public class LinkMan {
@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;
/**
* 配置联系人到客户的多对一关系
*
* 使用注解配置多对一关系
* 1.配置表关系
* @ManyToOne(targetEntity = Customer.class)
* 2.配置外键()
* @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
*
* 配置外键的过程,配置到了多的一方,就会在多的一方维护外键
*/
@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private Customer customer;
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
@Override
public String toString() {
return "LninkMan{" +
"lkmId=" + lkmId +
", lkmName='" + lkmName + '\'' +
", lkmGender='" + lkmGender + '\'' +
", lkmPhone='" + lkmPhone + '\'' +
", lkmMobile='" + lkmMobile + '\'' +
", lkmEmail='" + lkmEmail + '\'' +
", lkmPosition='" + lkmPosition + '\'' +
", lkmMemo='" + 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;
}
}
@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.声明关系
* @OnetoMany : 字面意思->一对多
* targetEntiry : 对方对象的字节码对象
* 2.配置外键(中间表)
* @JoinColumn(name:外键字段名,referencedColumnName:参照的主表的主键字段名称)
*
* 在客户实体类上(一)添加了外键配置,所以客户具备了维护外键的作用
*/
@OneToMany(targetEntity = LinkMan.class)
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private Set<LinkMan> linkMans = new HashSet<>();
public Set<LinkMan> getLinkMans() {
return linkMans;
}
public void setLinkMans(Set<LinkMan> linkMans) {
this.linkMans = linkMans;
}
public Customer() {
}
public Customer(String custName, String custSource, String custIndustry, String custLevel, String custAddress, String custPhone) {
this.custName = custName;
this.custSource = custSource;
this.custIndustry = custIndustry;
this.custLevel = custLevel;
this.custAddress = custAddress;
this.custPhone = custPhone;
}
@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custLevel='" + custLevel + '\'' +
", custAddress='" + custAddress + '\'' +
", custPhone='" + custPhone + '\'' +
'}';
}
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 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 getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
}
14.2.Dao层接口
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
}
public interface LinkManDao extends JpaRepository<LinkMan,Long>, JpaSpecificationExecutor<LinkMan> {
}
14.3.配置文件
<?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">
<!-- Spring和SpringDataJPA的配置 -->
<!-- 1.创建EntityManagerFactory对象交给Spring容器管理 -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 配置的扫描的包:实体类所在的包 -->
<property name="packagesToScan" value="per.xgt.pojo" />
<!-- jpa的实现方式 ->Hibernate -->
<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>
<!-- 注入JPA的配置信息 jpaProperties->加载JPA的基本配置信息和jpa实现方式的配置信息
hibernate.hbm2ddl.auto:自动创建数据库表
create : 每次都会重新创建数据库表
update : 有表不会重新创建数据库表,没有会创建表
-->
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
<!-- 2.创建数据库连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="password" value="root" />
<property name="user" value="root" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF8" />
<property name="driverClass" value="com.mysql.jdbc.Driver" />
</bean>
<!-- 3.整合SPringDataJPA -->
<jpa:repositories base-package="per.xgt.dao" transaction-manager-ref="transactionManager" entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
<!-- 4.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<!-- 5.声明式事务 -->
<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>
<!-- 5.aop -->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* per.xgt.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
</aop:config>
<!-- 6.配置包扫描 -->
<context:component-scan base-package="per.xgt" ></context:component-scan>
</beans>
14.4.关联保存
@Test
@Transactional
@Rollback(value = false)//设置不自动回滚
public void test1(){
//创建一个客户,创建一个联系人
Customer customer = new Customer();
customer.setCustName("客户1");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("联系人1");
/**
* 配置了关系:
*/
customer.getLinkMans().add(linkMan);
//或者
//linkMan.setCustomer(customer);
customerDao.save(customer);
linkManDao.save(linkMan);
}
备注
配置了多对一的(多方)的关联关系,当保存的时候,就已经对外键赋值了。
配置了一对多的关联关系(会发送一条update语句对外键赋值)
放弃一对多的外键维护权
/**
* 放弃外键维护权
* mappedBy : 对方配置关系的属性名称
*/
@OneToMany(mappedBy = "customer")
private Set<LinkMan> linkMans = new HashSet<>();
再插入测试
@Test
@Transactional
@Rollback(value = false)//设置不自动回滚
public void test2(){
//创建一个客户,创建一个联系人
Customer customer = new Customer();
customer.setCustName("客户1");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("联系人1");
linkMan.setCustomer(customer);
customer.getLinkMans().add(linkMan);
customerDao.save(customer);
linkManDao.save(linkMan);
}
14.5.删除和级联
级联
注意:
1.需要区分操作主体
2.需要在操作主体的实体类上,添加级联属性(需要添加到多表映射关系的注解上)
3.cascade(配置级联)
操作一个对象的同时操作他的关联对象
1.删除:如->删除一个客户的同时删除掉当前客户所有的联系人
2.添加:如->保存客户的同时保存联系人
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)
private Set<LinkMan> linkMans = new HashSet<>();
@Test
@Transactional
@Rollback(value = false)//设置不自动回滚
public void testCascadeAdd(){
Customer customer = new Customer();
customer.setCustName("客户2");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("联系人2");
linkMan.setCustomer(customer);
customer.getLinkMans().add(linkMan);
customerDao.save(customer);
}
级联删除
cascade
配置级联:
ALL:增删改查所有
MERGE:更新
PERSIST:保存
REMOVE:删除
14.6.多对多
可以直接建立实体类,让JPA帮我们建立数据库表
@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;
/**
* 配置用户到角色的多对多关系
* @ManyToMany 多对多
* targetEntity = Role.class 对方的实体类字节码
* @JoinTable 配置中间表
* name 中间表名
* joinColumns 当前对象在中间表的外键
* name:外键字段名
* referencedColumnName:参照的主表的主键字段名
* inverseJoinColumns 对方对象在中间表的外键
* name:外键字段名
* referencedColumnName:参照的主表的主键字段名
*
*/
@ManyToMany(targetEntity = Role.class)
@JoinTable(name = "sys_user_role",
joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
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;
}
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;
}
}
@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;
/**
* 配置多对多
*/
// @ManyToMany(targetEntity = User.class)
// @JoinTable(name = "sys_user_role",
// joinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")},
// inverseJoinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")}
// )
@ManyToMany(mappedBy = "roles")
private Set<User>users = new HashSet<>();
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
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;
}
}
保存
/**
* 保存一个用户,保存一个角色
*/
@Test
@Transactional
@Rollback(value = false)
public void test1(){
User user = new User();
user.setUserName("XGT");
Role role = new Role();
role.setRoleName("JAVA");
//配置用户到角色的关系,可以对中间表表中的数据进行维护
user.getRoles().add(role);
//配置角色到用户的关系,可以对中间表表中的数据进行维护
role.getUsers().add(user);
userDao.save(user);
roleDao.save(role);
}
备注
如果两边都维护中间表的话,同时两边实体类都set另一方并且save进数据库,会报主键重复的错误,所以需要一遍放弃维护中间表。方式在实体类中
级联添加
@ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
@JoinTable(name = "sys_user_role",
joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")}
)
private Set<Role> roles = new HashSet<>();
/**
* 级联:保存一个用户
*/
@Test
@Transactional
@Rollback(value = false)
public void test2(){
User user = new User();
user.setUserName("XGT");
Role role = new Role();
role.setRoleName("JAVA");
//配置用户到角色的关系,可以对中间表表中的数据进行维护
user.getRoles().add(role);
//配置角色到用户的关系,可以对中间表表中的数据进行维护
role.getUsers().add(user);
userDao.save(user);
}
级联删除
/**
* 级联:删除一个用户,同时删除他的关联对象
*/
@Test
@Transactional
@Rollback(value = false)
public void test3(){
//查询
User user = userDao.findOne(1l);
//删除用户
userDao.delete(user);
}
包括中间表都删除了,但是级联删除,要慎用!
14.7.多表(对象导航)查询
/**
* 对象导航查询:
* 通过此对象查询此对象所有的关联对象
*/
@Test
@Transactional//解决no session问题
public void test4(){
User user = userDao.getOne(1l);
//对象导航查询
Set<Role> roles = user.getRoles();
for (Role role : roles) {
System.out.println(role);
}
}
备注
一方查询多方默认使用的是延迟加载的形式查询的,调用get方法并不会立即发送查询,而实在使用关联对象的时候才会执行查询:延迟加载!
多方查询一方默认使用立即加载
如果不想使用延迟加载(不推荐)
修改配置:将延迟加载改为立即加载
fetch:配置到多表映射关系的注解上
@ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL,fetch = FetchType.LAZY)
EAGER立即加载
LAZY延迟加载