springData-JPA
1.概述:
JPA(Java Persistence API)是Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。它的出现主要是为了简化现有的持久化开发工作和整合ORM技术
ORM:通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。本质就是将数据从一种形式转换到另外一种形式。
结束现在Hibernate、TopLink等ORM框架各自为营的局面。JPA是在充分吸收了现有Hibernate、TopLink等ORM框架的基础上发展起来的,具有易于使用,伸缩性强等优点。
注意: JPA不是一种新的ORM框架,它的出现只是用于规范现有的ORM技术,它不能取代现有的Hibernate等ORM框架,相反,采用JPA开发时,我们仍将使用这些ORM框架,只是此时开发出来的应用不在依赖于某个持久化提供商。应用可以在不修改代码的情况下载任何JPA环境下运行,真正做到低耦合,可扩展的程序设计。类似于JDBC,在JDBC出现以前,我们的程序针对特性的数据库API进行编程,但是现在我们只需要针对JDBC API编程,这样能够在不改变代码的情况下就能换成其他的数据库。
JPA是一套规范,不是一套产品。Hibernate是一套产品,如果这些产品实现了JPA规范,那么我们可以叫它们为JPA的实现产品。使用JPA,就可以把我们的应用完全从Hibernate中解脱出来
- orm思想:操作实体类,就像是操作数据库表,建立两个映射关系(实体类和数据库表的映射关系),实现了orm的框架:mybatis(结果集映射),hibernate(实体类对象的关系映射)
- hibernate框架介绍
- JPA规范
- jpa的环境搭建,完成基本的crud操作
- 实体类和数据库表的关系映射
jpa的优势和特点:
1.标准化
2. 容错级特性的支持
3. 简单方便
4. 查询能力
5. 高级特性
JPA与hibernate的关系
JPA规范本质上就是一种ORM规范,注意不是ORM框架——因为JPA并未提供ORM实现,它只是制订了一些规范,提供了一些编程的API接口,但具体实现则由服务厂商来提供实现。
图形化理解jpa规范:
2.jpa入门案例
jpa操作步骤:
1:加载配置文件,创建工厂(实管理体类工厂)对象
EntityManagerFactory factory = Persistence.createEntityManagerFactory(“myDataSource”);
2:通过实体管理器类工厂获取实体管理器
2.1:createEntityManager()内部维护了很多的内容
2.2:维护了数据库的信息
2.3:维护了缓存信息
2.4:维护了所有的实体类管理器对象
2.5:再创建EntityManagerFactory的过程中会根据配置创建数据库表
注意: EntityManagerFactory:创建会非常的消耗资源
EntityManager entityManager = factory.createEntityManager();
特点:是一个线程安全的对象,多个线程访问同一个创建好的对象,不会出现问题。
如何解决 EntityManagerFactory的创建过程浪费资源(耗时的问题?)
解决方案:创建一个公共的EntityManagerFactory对象,使用静态代码块儿(封装为工具类)
3:获取事务对象,开启事务
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin(); //开始事务
transaction。commit(); //提交事务
4:完成增删改查操作
EntityManager entityManager = factory.createEntityManager();
EntityManager对象:实体类管理器
entityManager.presist():保存
entityManager.merge() ; 更新
entityManager.remove(); 删除
find/getRefrence:根据id查询
5:提交事务或者回滚事务
transaction.commit();
transaction.rollback();
6:释放资源
entityManager.close();
factory.close();
jpa的使用步骤:
目标:搭建jpa环境,使用jpa完成基本的增删改查
1.创建数据库表:
/*创建客户表*/
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;
2.创建maven工程,并且导入以下依赖
<?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>org.example</groupId>
<artifactId>springData-jpa</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.hibernate.version>5.4.28.Final</project.hibernate.version>
</properties>
<dependencies>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</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>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.3</version>
</dependency>
</dependencies>
</project>
3.配置jpa的核心配置文件
3.1:路径:配置到类路径下的一个叫做META-INF的文件下,
3.2:命名:persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!-- 需要配置persistence-unit节点:持久化单元
JTA:分布式管理
RESOURCE_LOCAL:本地事务管理
-->
<persistence-unit name="myDataSource" transaction-type="RESOURCE_LOCAL">
<!-- jpa的实现方式 -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 数据库信息 -->
<properties>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="2732195202"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/component?serverTimezone=GMT%2B8"/>
<!-- 可选配置 配置jpa实现方的配置信息 -->
<!-- 显示sql:输出到控制台 -->
<property name="hibernate.show_sql" value="true"/>
<!-- 显示sql:自动创建数据库表
create:程序可运行时创建数据库表,如果有表,先删除表再创建新表
update:程序运行时创建表,如果表已经存在,不会创建新的表
none:程序运行时,不会做任何改变
-->
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
4.新建实体类和数据库表做映射关系
/**
* @author compass
* @version 1.0
* @date 2021-03-04 14:27
*/
// 声明该类为实体类
@Entity
// 设置实体类和数据库表的映射关系
@Table(name = "cst_customer")
public class Customer {
@Id // 声明主键的配置 主键自增长
@GeneratedValue(strategy = GenerationType.IDENTITY) // 主键id :实体类属性和字段映射 声明主键的配置
@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;
}
public Customer() {
}
public Customer(Long custId, String custName, String custSource, String custLevel, String custIndustry, String custPhone, String custAddress) {
this.custId = custId;
this.custName = custName;
this.custSource = custSource;
this.custLevel = custLevel;
this.custIndustry = custIndustry;
this.custPhone = custPhone;
this.custAddress = custAddress;
}
@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custLevel='" + custLevel + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custPhone='" + custPhone + '\'' +
", custAddress='" + custAddress + '\'' +
'}';
}
}
5.测试:想客户表中插入一条数据
/**
* @author compass
* @version 1.0
* @date 2021-03-04 14:52
*/
/**
* jpa的使用步骤:
* 1:加载配置文件,创建工厂(实管理体类工厂)对象
* 2:通过实体管理器类工厂获取实体管理器
* 3:获取事务对象,开启事务
* 4:完成增删改查操作
* 5:提交事务或者回滚事务
* 6:释放资源
*/
public class JpaTest {
@Test
void insert(){
//1:加载配置文件,创建工厂(实管理体类工厂)对象
EntityManagerFactory factory = Persistence.createEntityManagerFactory("myDataSource");
//2:通过实体管理器类工厂获取实体管理器
EntityManager entityManager = factory.createEntityManager();
//3:获取事务对象,开启事务
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
//添加一个客户到数据库表中
Customer customerObject = new Customer();
customerObject.setCustId(null);
customerObject.setCustName("compass");
customerObject.setCustSource("随机");
customerObject.setCustLevel("小白");
customerObject.setCustIndustry("java开发");
customerObject.setCustAddress("未知");
customerObject.setCustPhone("123456");
// 4:保存一条客户信息到数据库表
entityManager.persist(customerObject);
//5:提交事务或者回滚事务
transaction.commit();
// 6:释放资源
entityManager.close();
factory.close();
}
}
3.主键生成策略
@GeneratedValue(strategy = GenerationType.IDENTITY) // 主键id :实体类属性和字段映射 声明主键的配置
-
GenerationType.IDENTITY:主键自增长:底层数据库必须支持自增长
-
GenerationType.SEQUENCE:底层数据库必须支持序列(oracle)
-
GenerationType.TABLE: jpa提供的一种机制,通过一张数据库表的形式完成自增,底层数据库不需要支持自增或序列
-
GenerationType.AUTO:由程序自动选择主键生成策略
4.工具类的提取
解决 EntityManagerFactory的创建过程浪费资源(耗时的问题)。
/**
* @author compass
* @version 1.0
* @date 2021-03-04 17:10
*/
public class JpaUtils {
private static EntityManagerFactory factory;
static {
//1.加载配置文件,创建entityManagerFactory
factory = Persistence.createEntityManagerFactory("myDataSource");
}
/**
* 获取EntityManager对象
*/
public static EntityManager getEntityManager() {
return factory.createEntityManager();
}
}
延迟加载和立即加载(find和getReference的区别)
Customer customer = entityManager.find(Customer.class, 1l);
方式1: find(需要查询的实体类字节码, 需要查询的id);
1.查询的对象就是当前客户对象本身
2.在调用find方法的时候,就会发送sql语句到数据库进行查询
优先使用方式2:因为这样可以大大的提高性能
Customer customer = entityManager.getReference(Customer.class, 1l);
方式2: getRefresh(需要查询的实体类字节码, 需要查询的id);
1.使用getRefresh方法是一个动态代理对象
2.调用getRefresh方法不会立即发送sql语句进行查询
当调用查询对象结果集的时候才会发送sql语句到数据库进行查询
延迟加载:得到的时候一个动态代理对象,什么时候用,什么时候发送sql进行查询
5.jpa的增删改查操作
/**
* @author compass
* @version 1.0
* @date 2021-03-04 14:52
*/
/**
* jpa的使用步骤:
* 1:加载配置文件,创建工厂(实管理体类工厂)对象
* 2:通过实体管理器类工厂获取实体管理器
* 3:获取事务对象,开启事务
* 4:完成增删改查操作
* 5:提交事务或者回滚事务
* 6:释放资源
*/
public class JpaTest {
@Test
void insert1(){
//1:加载配置文件,创建工厂(实管理体类工厂)对象
EntityManagerFactory factory = Persistence.createEntityManagerFactory("myDataSource");
//2:通过实体管理器类工厂获取实体管理器
EntityManager entityManager = factory.createEntityManager();
//3:获取事务对象,开启事务
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
//添加一个客户到数据库表中
Customer customerObject = new Customer();
customerObject.setCustId(null);
customerObject.setCustName("compass");
customerObject.setCustSource("随机");
customerObject.setCustLevel("小白");
customerObject.setCustIndustry("java开发");
customerObject.setCustAddress("未知");
customerObject.setCustPhone("123456");
// 4:保存一条客户信息到数据库表
entityManager.persist(customerObject);
//5:提交事务或者回滚事务
transaction.commit();
// 6:释放资源
entityManager.close();
factory.close();
}
@Test
void insert2(){
//通过工具类获取entityManager对象
EntityManager entityManager = JpaUtils.getEntityManager();
//获取事务对象
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
//向数据库表中插入条数据
Customer customerObject = new Customer();
customerObject.setCustId(null);
customerObject.setCustName("compass");
customerObject.setCustSource("随机");
customerObject.setCustLevel("小白");
customerObject.setCustIndustry("java开发");
customerObject.setCustAddress("未知");
customerObject.setCustPhone("123456");
entityManager.persist(customerObject);
transaction.commit();
}
@Test
void delete(){
//通过工具类 获取entityManager 对象
EntityManager entityManager = JpaUtils.getEntityManager();
//获取事务对象
EntityTransaction transaction = entityManager.getTransaction();
//开启事务
transaction.begin();
//先查询需要删除的对象
Customer customer = entityManager.find(Customer.class, 2l);
//把查询出来的对象传递进去 进行删除
entityManager.remove(customer);
// 提交事务
transaction.commit();
}
@Test
void update(){
// 通过工具类获取 entityManager 对象
EntityManager entityManager = JpaUtils.getEntityManager();
//获取事务对象
EntityTransaction transaction = entityManager.getTransaction();
//开启事务
transaction.begin();
//先将需要修改的对象查询出来
Customer customer = entityManager.find(Customer.class, 1l);
//修改customer对象的属性
customer.setCustName("卡夫卡");
//传入修改后的对象进行修改数据库表中的记录
Customer queryOutCustomer = entityManager.merge(customer);
//提交事务
transaction.commit();
}
@Test
void query(){
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
/**
* 方式1: find(需要查询的实体类字节码, 需要查询的id);
* 1.查询的对象就是当前客户对象本身
* 2.在调用find方法的时候,就会发送sql语句到数据库进行查询
*/
// Customer customer = entityManager.find(Customer.class, 1l);
/**
* 方式2: find(需要查询的实体类字节码, 需要查询的id);
* 1.使用getRefresh方法是一个动态代理对象
* 2.调用getRefresh方法不会立即发送sql语句进行查询
* 当调用查询对象结果集的时候才会发送sql语句到数据库进行查询
* 延迟加载:得到的时候一个动态代理对象,什么时候用,什么时候发送sql进行查询
*/
Customer customer = entityManager.getReference(Customer.class, 1l);
System.out.println(customer);
transaction.commit();
}
}
6.jpql操作
接下需要用到的数据库表:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for cst_customer
-- ----------------------------
DROP TABLE IF EXISTS `cst_customer`;
CREATE TABLE `cst_customer` (
`cust_id` bigint NOT NULL AUTO_INCREMENT,
`cust_address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`cust_industry` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`cust_level` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`cust_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`cust_phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`cust_source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`cust_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of cst_customer
-- ----------------------------
INSERT INTO `cst_customer` VALUES (1, '未知', 'java开发', '小白', '卡夫卡', '123456', '不详');
INSERT INTO `cst_customer` VALUES (2, '未知', 'java开发', '小白', 'root', '123456', '不详');
INSERT INTO `cst_customer` VALUES (3, '未知', 'linux开发', '小白', 'user', '123456', '随机');
INSERT INTO `cst_customer` VALUES (4, '未知', 'linux开发', '小白', 'test', '123456', '随机');
INSERT INTO `cst_customer` VALUES (5, '未知', 'html开发', '小白', 'jack', '123456', '随机');
INSERT INTO `cst_customer` VALUES (6, '未知', 'html开发', '小白', 'tom', '123456', '随机');
INSERT INTO `cst_customer` VALUES (7, '未知', 'JavaScript开发', '小白', 'admin', '123456', '随机');
INSERT INTO `cst_customer` VALUES (8, '未知', 'JavaScript开发', '小白', 'compass', '123456', '随机');
INSERT INTO `cst_customer` VALUES (10, '未知', 'html开发', '小白', 'exclusive', '123456', '随机');
SET FOREIGN_KEY_CHECKS = 1;
/**
* @author compass
* @version 1.0
* @date 2021-03-04 20:33
*/
public class JpqlTest {
/**
* jpql :查询全部
* jpql:from Customer
* sql:select * from cst_customer
*/
@Test
void queryAll(){
//1.获取 entityManager 对象
EntityManager entityManager = JpaUtils.getEntityManager();
//2.开启事务
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
//3.查询全部
String sql="from com.compass.hibernate.Entity.Customer";
//创Query对象 :执行jpql的对象
Query query = entityManager.createQuery(sql);
//封装查询结果集
List customerList = query.getResultList();
for (Object customer : customerList) {
System.out.println(customer);
}
//4.提交事务
transaction.commit();
//5.释放资源
entityManager.close();
}
@Test
void queryOrderBy(){
//1.获取 entityManager 对象
EntityManager entityManager = JpaUtils.getEntityManager();
//2.开启事务
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
//3.查询全部 并且按升序排列 asc:升序 desc:降序
String sql="from com.compass.hibernate.Entity.Customer order by custId asc";
//创Query对象 :执行jpql的对象
Query query = entityManager.createQuery(sql);
//封装查询结果集
List customerList = query.getResultList();
for (Object customer : customerList) {
System.out.println(customer);
}
//4.提交事务
transaction.commit();
//5.释放资源
entityManager.close();
}
@Test
void queryCount(){
//1.获取 entityManager 对象
EntityManager entityManager = JpaUtils.getEntityManager();
//2.开启事务
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
//3.统计表中的记录总数
String sql="select count(custId) from Customer";
//创Query对象 :执行jpql的对象
Query query = entityManager.createQuery(sql);
//封装查询结果集
List customerList = query.getResultList();
for (Object customer : customerList) {
System.out.println(customer);
}
//4.提交事务
transaction.commit();
//5.释放资源
entityManager.close();
}
@Test
void queryPaging(){
//1.获取 entityManager 对象
EntityManager entityManager = JpaUtils.getEntityManager();
//2.开启事务
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
//3.查询全部 并且设置分页条件
String sql="from Customer where custId>2";
//创Query对象 :执行jpql的对象
Query query = entityManager.createQuery(sql);
//设置分页查询的起始索引
query.setFirstResult(0);
//设置分页查询每页显示的条数
query.setMaxResults(4);
//封装查询结果集
List customerList = query.getResultList();
for (Object customer : customerList) {
System.out.println(customer);
}
//4.提交事务
transaction.commit();
//5.释放资源
entityManager.close();
}
@Test
void queryGroupBy(){
//1.获取 entityManager 对象
EntityManager entityManager = JpaUtils.getEntityManager();
//2.开启事务
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
//3.查询全部 并且按 custIndustry 进行分组
String sql="from Customer group by custIndustry ";
//创Query对象 :执行jpql的对象
Query query = entityManager.createQuery(sql);
//封装查询结果集
List customerList = query.getResultList();
for (Object customer : customerList) {
System.out.println(customer);
}
//4.提交事务
transaction.commit();
//5.释放资源
entityManager.close();
}
@Test
void queryLike(){
//1.获取 entityManager 对象
EntityManager entityManager = JpaUtils.getEntityManager();
//2.开启事务
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
//3.查询全部模糊查询
String sql="from Customer where custIndustry like ?1";
//创Query对象 :执行jpql的对象
Query query = entityManager.createQuery(sql);
//设置模糊查询占位符
query.setParameter(1,"java%");
//封装查询结果集
List customerList = query.getResultList();
for (Object customer : customerList) {
System.out.println(customer);
}
//4.提交事务
transaction.commit();
//5.释放资源
entityManager.close();
}
}
7.springDataJpa
1.概述:
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!
Spring Data JPA 让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现,在实际的工作工程中,推荐使用Spring Data JPA + ORM(如:hibernate)完成操作,这样在切换不同的ORM框架时提供了极大的方便,同时也使数据库层操作更加简单,方便解耦。
JPA是一套规范,内部是有接口和抽象类组成的。hibernate是一套成熟的ORM框架,而且Hibernate实现了JPA规范,所以也可以称hibernate为JPA的一种实现方式,我们使用JPA的API编程,意味着站在更高的角度上看待问题(面向接口编程)。
Spring Data JPA是Spring提供的一套对JPA操作更加高级的封装,是在JPA规范下的专门用来进行数据持久化的解决方案。
2.springDataJpa快速入门
环境搭建:
使用Spring Data JPA,需要整合Spring与Spring Data JPA,并且需要提供JPA的服务提供者hibernate,所以需要导入spring相关坐标,hibernate坐标,数据库驱动坐标等。
<properties>
<spring.version>4.2.4.RELEASE</spring.version>
<hibernate.version>5.0.7.Final</hibernate.version>
<slf4j.version>1.7.30</slf4j.version>
<log4j.version>2.12.1</log4j.version>
<c3p0.version>0.9.1.2</c3p0.version>
<mysql.version>8.0.21</mysql.version>
</properties>
<dependencies>
<!-- junit单元测试 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</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>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</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>${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 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
2.1:在Resources目录下新建applicationContext.xml文件,并且配置以下内容
<?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 和 spring data jpa 的配置-->
<!--1.创建entityManagerFactory对象交给spring容器管理-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- packagesToScan :实体类所在包 -->
<property name="packagesToScan" value="com.compass.hibernate.Entity"/>
<!--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>
</bean>
<!--2. 创建数据库连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"/>
<property name="password" value="root"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/component?serverTimezone=GMT%2B8"/>
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
</bean>
<!--3.整合spring dataJpa base-package:dao包所在位置 -->
<jpa:repositories base-package="com.compass.hibernate.dao" transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory" />
<!--4.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- 4.txAdvice-->
<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(* com.compass.hibernate.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
</aop:config>
<!--5.声明式事务 -->
<!-- 6. 配置包扫描,使用注解开发 -->
<context:component-scan base-package="com.compass.hibernate" />
</beans>
2.2编写一个符合springDataJpa的dao接口
只写接口,不需要写实现类
dao接口需要继承两个接口(JpaSpeciffcationExecutor和JpaRepository)
需要提供接口的泛型,也就实体类的class
源码可知:JpaRepository这个接口中实现了基本的增删改查方法:
/**
* 只要实现这两个接口:就具备了 基本的增删改查
* JpaRepository<实体类Class,主键Id类型>
* JpaSpecificationExecutor<实体类Class>
*/
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
}
2.3:编写实体类和数据库表实现映射关系
/**
* @author compass
* @version 1.0
* @date 2021-03-04 14:27
*/
// 声明该类为实体类
@Entity
// 设置实体类和数据库表的映射关系
@Table(name = "cst_customer")
public class Customer {
/**
* @GeneratedValue(strategy = GenerationType.IDENTITY) // 主键id :实体类属性和字段映射 声明主键的配置
* GenerationType.IDENTITY:主键自增长:底层数据库必须支持自增长
* GenerationType.SEQUENCE:底层数据库必须支持序列(oracle)
* GenerationType.TABLE: jpa提供的一种机制,通过一张数据库表的形式完成自增,底层数据库不需要支持自增或序列
* GenerationType.AUTO:由程序自动选择主键生成策略
*/
@Id // 声明主键的配置 主键自增长
@GeneratedValue(strategy = GenerationType.IDENTITY) // 主键id :实体类属性和字段映射 声明主键的配置
@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;
}
public Customer() {
}
public Customer(Long custId, String custName, String custSource, String custLevel, String custIndustry, String custPhone, String custAddress) {
this.custId = custId;
this.custName = custName;
this.custSource = custSource;
this.custLevel = custLevel;
this.custIndustry = custIndustry;
this.custPhone = custPhone;
this.custAddress = custAddress;
}
@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custLevel='" + custLevel + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custPhone='" + custPhone + '\'' +
", custAddress='" + custAddress + '\'' +
'}';
}
}
2.4springDataJpa 增删改查操作:
/**
* @author compass
* @version 1.0
* @date 2021-03-05 21:25
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class SpringDataJpaTest {
@Autowired
private CustomerRepository customerRepository;
@Test
public void queryOne(){
Customer customer = customerRepository.findOne(1l);
System.out.println(customer);
}
@Test
public void queryAll(){
List<Customer> list = customerRepository.findAll();
for (Customer customer : list) {
System.out.println(customer);
}
}
@Test
public void insert(){
Customer customer = new Customer();
customer.setCustId(null);
customer.setCustAddress("未知");
customer.setCustIndustry("ui设计");
customer.setCustLevel("大佬");
customer.setCustPhone("123456");
customer.setCustSource("随机");
customer.setCustName("卡夫瑞");
/**
* save: 更新或者修改
* 当传入的对象有主键id属性的时候就为修改,如果没有id属性的时候则为保存
*/
customerRepository.save(customer);
}
@Test
public void update(){
Customer customer = new Customer();
customer.setCustId(9l);
customer.setCustAddress("未知");
customer.setCustIndustry("运维");
customer.setCustLevel("大佬");
customer.setCustPhone("123456");
customer.setCustSource("随机");
customer.setCustName("卡夫瑞");
/**
* save: 更新或者修改
* 当传入的对象有主键id属性的时候就为修改,如果没有id属性的时候则为保存
*/
Customer selectLaterCustomer = customerRepository.save(customer);
}
@Test
public void delete(){
customerRepository.delete(9l);
}
@Test
public void count(){
long count = customerRepository.count();
System.out.println(count);
}
@Test
public void exists(){
// 判断id 为 1 的记录是否存在
boolean flag = customerRepository.exists(1l);
System.out.println(flag);
}
/**getOne和findOne的区别
* getOne: 立即加载,立即发送sql到数据库
* findOne:懒加载:等需要调用结果集的时候才会发送sql到数据库查询数据
*/
}
8.springDataJpa执行原理:
1.通过JdkDynamicAopProxy的invoke方法创建了一个动态代理对象(SimpleJpaRepository)
2. SimpleJpaRepository当中封装了JPA的操作(借助JPA的api完成数据库的CURD)
3. 通过hibernate完成数据库操作(封装了jdbc)
9.Spring Data JPA的查询方式
1.首先需要继承者两个接口:
1.1.使用Query注解自定义查询方法:
public interface CustomerRepository extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
/**
* 使用jpql Query注解查询:根据客户名称查询
* jpql: from Customer where customerName=?
*/
@Query(value="from Customer where custName= ?1 ")
Customer findJpqlBycustomerName(String customerName);
}
1.2Test自定义的查询方法:
@Test
public void queryByCustomerName(){
Customer customer = customerRepository.findJpqlBycustomerName("root");
System.out.println(customer);
}
2.Query注解sql查询:
2.1.在Dao接口中写上此方法:
nativeQuery : false(使用jpql查询,默认值) | true(使用本地查询 :sql查询)
/**
* nativeQuery : false(使用jpql查询,默认值) | true(使用本地查询 :sql查询)
* sql查询: 使用sql的形式查询
* @return
*/
@Query(value="select * from cst_customer" ,nativeQuery=true)
List<Customer> queryAllSql();
2.测试…
3.Query注解sql查询(带参数):
/**
* 占位符:?1:表示从参数列表中取第一个参数作为占位符的值 ?1 == industry
* 查询 java开发
* @return
*/
@Query(value="select * from cst_customer where cust_industry=?1",nativeQuery=true)
List<Customer> queryAllByIndustrySql(String industry);
4.方法命名基本查询:
根据方法名称来查询:对jpql规范更加深层次的封装(定义在Dao接口中)
1.我们只需要根据SpringDataJpa提供的方法名称定义方法,不需要在配置jpql语句,就可以进行查询
2findBy:代表查询 +对象中的属性名称(首字母大写,也就是查询条件)+ 查询方式(isNull ,Like等)
3.在springDataJpa 会根据名称进行分析 findBy from 实体类, 属性名称 where custName=?
4.如果是多个条件使用And进行拼接 :findByCustNameAndCustIndustry
/**
* 根据方法名称来查询:对jpql规范更加深层次的封装
* 1.我们只需要根据SpringDataJpa提供的方法名称定义方法,不需要在配置jpql语句,就可以进行查询
* 2. findBy:代表查询 +对象中的属性名称(首字母大写,也就是查询条件)
* 3.在springDataJpa 会根据名称进行分析 findBy from 实体类, 属性名称 where custName=?
*
*/
List<Customer> findByCustName(String custName);
5.方法命名条件查询
dao接口代码:
/**
* 根据 industry 进行模糊查询
* 1.我们只需要根据SpringDataJpa提供的方法名称定义方法,不需要在配置jpql语句,就可以进行查询
* 2. findBy:代表查询 +对象中的属性名称(首字母大写,也就是查询条件)+ 查询方式(isNull ,Like等)
* 3.在springDataJpa 会根据名称进行分析 findBy from 实体类, 属性名称 where custName=?
* 4.如果是多个条件使用And进行拼接 :findByCustNameAndCustIndustry
*/
List<Customer> findByCustIndustryLike(String industry);
测试代码:
@Test
public void findByIndustryLike(){
List<Customer> industryList= customerRepository.findByCustIndustryLike("j%");
for (Customer customer : industryList) {
System.out.println(customer);
}
}
6.多条件查询
1、我们只需要根据SpringDataJpa提供的方法名称定义方法,不需要在配置jpql语句,就可以进行查询
2、 findBy:代表查询 +对象中的属性名称(首字母大写,也就是查询条件)+ 查询方式(isNull ,Like等)+多条件连接符( or | and)
3、在springDataJpa 会根据名称进行分析 findBy from 实体类, 属性名称 where custName=?
4、如果是多个条件使用And进行拼接 :findByCustNameAndCustIndustry
//根据 industry 进行模糊查询
List<Customer> findByCustIndustryAndCustSource(String industry , String source);
10.Specification动态查询
Specification动态查询介绍:
有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。
Specification:方法列表:
1、T findOne(Specification spec); //查询单个对象
2、List findAll(Specification spec); //查询列表
3、查询全部,分页
pageable:分页参数
返回值:分页pageBean(page:是springdatajpa提供的)
Page findAll(Specification spec, Pageable pageable);
4、查询列表
Sort:排序参数
List findAll(Specification spec, Sort sort);
5、ong count(Specification spec);//统计查询
Specification:查询条件
自定义我们自己的Specification的实现类
predicate toPerdicate(root root,CriteriaQuery<?> query,CriterBuilder builder);
封装查询条件:
root:查询的根对象,查询的任何属性都可以从跟对象中获取
CriteriaQuery:顶层查询对象,自定义查询方式
CriterBuilde:查询构造器(内置很多查询条件)
1、查询单个对象
// 查询单个对象
@Test
public void queryByCustomerName(){
/**
* Specification:实现Specification接口<需要查询的对象类型>
* Root:获取需要查询的对象属性
* CriteriaQuery:顶层查询对象,自定义查询方式
* CriteriaBuilder:构造查询条件,内部封装了很多查询条件
*/
Specification specification = new Specification<Customer>() {
@Override // 采用匿名内部类,构建查询条件
public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
//1.获取需要比较的属性名称(注意不是字段名)
Path custName = root.get("custName");
//2.构造查询条件
Predicate predicate = criteriaBuilder.equal(custName, "root");//进行精准匹配(比较的属性,比较属性的取值)
return predicate;
}
};
//根据客户名称查询
Customer customer = customerRepository.findOne(specification);
System.out.println(customer);
}
2.多条件查询
//多条件查询
@Test
public void moreConditionSelect(){
Specification specification = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path custName = root.get("custName");
Path source = root.get("custSource");
Predicate predicate1 = criteriaBuilder.equal(custName, "root");
Predicate predicate2 = criteriaBuilder.equal(source, "不详");
//将多个查询条件组合到一起(or | and )
Predicate predicate = criteriaBuilder.and(predicate1, predicate2);
return predicate;
}
};
Customer customer = customerRepository.findOne(specification);
System.out.println(customer);
}
3.模糊查询
/**
* 模糊查询 根据产业 industry字段进行模糊查询
* gt ,lt,ge,le,like 得到path对象,根据path对象知道比较的参数类型 再去进行比较
* 指定参数类型path.as(参数类型的字节码对象)
*/
@Test
public void selectLike(){
Specification specification = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path industry = root.get("custIndustry");
Predicate predicate = criteriaBuilder.like(industry.as(String.class), "java%");
return predicate;
}
};
List<Customer> customerList = customerRepository.findAll(specification);
for (Customer customer : customerList) {
System.out.println(customer);
}
}
4.模糊查询 根据产业 industry字段进行模糊查询 + 排序 +分页
/**
* 模糊查询 根据产业 industry字段进行模糊查询 + 排序 +分页
* gt ,lt,ge,le,like 得到path对象,根据path对象知道比较的参数类型 再去进行比较
* 指定参数类型path.as(参数类型的字节码对象)
* Sort:排序对象 参数1:排序方式(asc,desc) 参数2:按照那个字段进行排序
* 在创建 PageRequest 对象的时候 需要通过构造方法传递两个参数 参数1: 查询索引位置(从0开始),参数2:每页的数据大小
* getTotalElements: 获取查询出来的数据中有多少条数据
* getContent:获取查询出来的数据结果集
* totalPages:获取总页数
*/
@Test
public void Page() {
Specification specification = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path industry = root.get("custIndustry");
Predicate predicate = criteriaBuilder.like(industry.as(String.class), "java%");
return predicate;
}
};
Sort sort = new Sort(Sort.Direction.ASC,"custId");
//在创建 PageRequest 对象的时候 需要通过构造方法传递两个参数 参数1: 查询索引位置(从0开始),参数2:每页的数据大小
Pageable pageable = new PageRequest(0,2,sort);
Page<Customer> page = customerRepository.findAll(specification,pageable);
for (Customer customer : page) {
System.out.println(customer);
}
}
}
11.多表之间的关系映射和操作
11.1表与表之间的关系
表关系
一对一(单张表,一个主键)
一对多:(两张表,两个主键,一个外键)
一的一方:主表(一个主键)
多的一方:从表(一个主键,新建一个字段组作为外键,取值来源于主表的主键)
多对多(三张表,两个主键,两个外键)
中间表:中间表两个外键,两个外键分别关联另外的两张表
11.2、实体类中你的关系
1.继承关系
2.包含关系
orm框架分析步骤:
1.分析表与表之间的关系
2.确定表的关系(外键描述一对一,中间表描述多对多)
3.实体类中描述表关系(包含关系)
4.配置实体类和数据库表的映射关系
11.1一对多|多对一 案例:
员工和公司的关系
公司:一家公司
员工:这家公司的员工
一个员工对应一个公司,一个公司有多个员工
公司表为主表:
员工表为从表:
公司实体类(Company):一对多
/**
* @author compass
* @version 1.0
* @date 2021-03-07 17:55
*/
@Entity()
@Table(name = "t_company")
public class Company {
@Id
@Column(name = "comp_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long compId;
@Column(name = "comp_Name")
private String compName;
@Column(name = "comp_email")
private String compEmail;
@Column(name = "comp_phone")
private String compPhone;
/**
* 配置员工和公司的关系映射(一对多)
* @OneToMany: 一对多关系 targetEntity:对方对象的字节码(多的一方)
*
* 配置外键:
* @JoinColumn : name :外键名称 referencedColumnName :外键的取值来源
*/
@OneToMany(targetEntity = Employee.class)
@JoinColumn(name = "comp_emp_id",referencedColumnName = "comp_id")
private Set<Employee> companies = new HashSet();
public Company() {
}
public Long getCompId() {
return compId;
}
public void setCompId(Long compId) {
this.compId = compId;
}
public String getCompName() {
return compName;
}
public void setCompName(String compName) {
this.compName = compName;
}
public String getCompEmail() {
return compEmail;
}
public void setCompEmail(String compEmail) {
this.compEmail = compEmail;
}
public String getCompPhone() {
return compPhone;
}
public void setCompPhone(String compPhone) {
this.compPhone = compPhone;
}
public Set<Employee> getCompanies() {
return companies;
}
public void setCompanies(Set<Employee> companies) {
this.companies = companies;
}
public Company(Long compId, String compName, String compEmail, String compPhone, Set<Employee> companies) {
this.compId = compId;
this.compName = compName;
this.compEmail = compEmail;
this.compPhone = compPhone;
this.companies = companies;
}
@Override
public String toString() {
return "Company{" +
"compId=" + compId +
", compName='" + compName + '\'' +
", compEmail='" + compEmail + '\'' +
", compPhone='" + compPhone + '\'' +
", companies=" + companies +
'}';
}
}
员工实体类(Employee):多对一
@Entity
@Table(name = "t_employee")
public class Employee {
/**
* 1.分析表与表之间的关系
* 2.确定表的关系(外键描述一对一,中间表描述多对多)
* 3.实体类中描述表关系(包含关系)
* 4.配置实体类和数据库表的映射关系
* 公司表为主表:
* 员工表为从表:
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "emp_id")
private Long empId;
@Column(name = "emp_name")
private String empName;
@Column(name = "emp_phone")
private String empPhone;
@Column(name = "emp_job")
private String empJob;
/**
* 配置多对一关系:
* targetEntity:对方的字节码值
* @JoinColumn: name:外键名称 referencedColumnName:外键的去值来源
*/
@ManyToOne(targetEntity = Company.class)
@JoinColumn(name = "comp_emp_id",referencedColumnName = "comp_id")
private Company company;
public Long getEmpId() {
return empId;
}
public void setEmpId(Long empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getEmpPhone() {
return empPhone;
}
public void setEmpPhone(String empPhone) {
this.empPhone = empPhone;
}
public String getEmpJob() {
return empJob;
}
public void setEmpJob(String empJob) {
this.empJob = empJob;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public Employee(Long empId, String empName, String empPhone, String empJob, Company company) {
this.empId = empId;
this.empName = empName;
this.empPhone = empPhone;
this.empJob = empJob;
this.company = company;
}
public Employee() {
}
@Override
public String toString() {
return "Employee{" +
"empId=" + empId +
", empName='" + empName + '\'' +
", empPhone='" + empPhone + '\'' +
", empJob='" + empJob + '\'' +
", company=" + company +
'}';
}
}
插入数据:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class OneToManyTest {
@Autowired
private CompanyRepository companyRepository;
@Autowired
private EmployeeRepository employeeRepository;
/**
* 此时的 保存一个员工到员工表中,是没有外键id的,两张表此时是独立存在的,员工的外键为空
* 实体类中没有配置关系?
*/
@Test
@Transactional
@Rollback(false)
public void TestAddEmployeeAndCompany(){
Employee employee = new Employee();
employee.setEmpJob("开发");
employee.setEmpName("admin");
employee.setEmpPhone("123456");
Company company = new Company();
company.setCompName("未知");
company.setCompPhone("1234567");
company.setCompEmail("weizhi@qq.com");
// 解决方案1: Employee插入数据时没有外键
// company.getCompanies().add(employee);
// 解决方案2: Employee插入数据时没有外键
Employee saveEmployee = employeeRepository.save(employee);
System.out.println(saveEmployee);
Company saveCompany = companyRepository.save(company);
System.out.println(saveCompany);
}
}
一对多的一方:放弃外键维护
@OneToMany(mappedBy = "company") //放弃外键维权 ,参照 company 的配置进行
private Set<Employee> companies = new HashSet();
一对多的级联操作
/**
* 配置员工和公司的关系映射(一对多)
* @OneToMany: 一对多关系 targetEntity:对方对象的字节码(多的一方)
*
* 配置外键:
* @JoinColumn : name :外键名称 referencedColumnName :外键的取值来源
* mappedBy:对方的关系名称
*
* cascade : 配置级联(可以配置到设置多表的映射关系的注解上)
* CascadeType.all : 所有
* MERGE :更新
* PERSIST :保存
* REMOVE :删除
*/
/*@OneToMany(targetEntity = Employee.class)
@JoinColumn(name = "comp_emp_id",referencedColumnName = "comp_id")*/
@OneToMany(mappedBy = "company",cascade = CascadeType.ALL) //放弃外键维权 ,参照 company 的配置进行
private Set<Employee> companies = new HashSet();
级联添加:
//级联添加 (添加公司的时候,同时也把员工添加上,并且关联上外键)
@Test
@Transactional
@Rollback(false)
public void cascadeInsertCompanyById(){
Employee employee = new Employee();
employee.setEmpJob("开发");
employee.setEmpName("admin");
employee.setEmpPhone("123456");
Company company = new Company();
company.setCompName("未知");
company.setCompPhone("1234567");
company.setCompEmail("weizhi@qq.com");
company.getCompanies().add(employee);
employee.setCompany(company);
companyRepository.save(company);
}
级联删除:
//级联删除 (删除一个客户的时候,删除员工表中外键和客户表主键关联的数据)
@Test
@Transactional
@Rollback(false)
public void cascadeDeleteCompanyById(){
companyRepository.delete(13l);
}
11.2多对多案例:
用户和角色的关系:
一个用户有多种角色
一个角色可以有多个用户
三个表:
一个用户表:sys_user :一个主键
一个角色表:sys_role : 存放sys_user和sys_role的主键,分别关联另外的两张表
中间表:sys_user_role: 一个主键
用户实体类:
/**
* @author compass
* @version 1.0 用户表
* @date 2021-03-08 12:15
*/
@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 = "user_password")
private String userPassword;
/**
* 配置用户到角色的多对多关系
* 1.声明表关系
* 2.描述表关系,配置中间表 (中间表包含两个外键)
* 3. @ManyToMany:配置多对多关系 targetEntity : 对方的实体类字节码
* 4. @JoinTable: name:中间表的名称
* 5. @JoinTable: joinColumns:配置当前表在中间表的外键 inverseJoinColumns:对方对象在中间表的外键
* referencedColumnName:参照的是主表的主键名称
* 6.cascade : 配置级联(可以配置到设置多表的映射关系的注解上)
*
* CascadeType.all : 所有
* MERGE :更新
* PERSIST :保存
* REMOVE :删除
*
*/
@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 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 String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", userPassword='" + userPassword + '\'' +
", roles=" + roles +
'}';
}
}
角色实体类:
/**
* @author compass
* @version 1.0 角色表
* @date 2021-03-08 12:15
*/
@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")}
)
private Set<User> users = new HashSet<>();
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;
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
@Override
public String toString() {
return "Role{" +
"roleId=" + roleId +
", roleName='" + roleName + '\'' +
", users=" + users +
'}';
}
}
测试保存,并且生成数据库表:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ManyToManyTest {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Test
@Transactional
@Rollback(false)
public void initTest(){
User user = new User();
user.setUserName("admin");
user.setUserPassword("123");
Role role = new Role();
role.setRoleName("普通用户");
userRepository.save(user);
roleRepository.save(role);
}
}
多对多,让其一方放弃维护权:
//配置多对多
@ManyToMany(mappedBy = "roles") //让角色表放弃维护权
private Set<User> users = new HashSet<>();
多对多级联操作:在User实体类中描述级联配置
/**
* 配置用户到角色的多对多关系
* 1.声明表关系
* 2.描述表关系,配置中间表 (中间表包含两个外键)
* 3. @ManyToMany:配置多对多关系 targetEntity : 对方的实体类字节码
* 4. @JoinTable: name:中间表的名称
* 5. @JoinTable: joinColumns:配置当前表在中间表的外键 inverseJoinColumns:对方对象在中间表的外键
* referencedColumnName:参照的是主表的主键名称
* 6.cascade : 配置级联(可以配置到设置多表的映射关系的注解上)
*
* CascadeType.all : 所有
* MERGE :更新
* PERSIST :保存
* REMOVE :删除
*
*/
@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(false)
public void CascadeInsertTest(){
User user = new User();
user.setUserName("admin");
user.setUserPassword("123");
Role role = new Role();
role.setRoleName("普通用户");
/**
* 报错 : 解决方案 :让其中一方 放弃维护权
* 多对多放弃维护权,被动的一方放弃维护权
* 让用户表去维护角色表,否则在重写toString 方法的时候会报异常
*/
user.getRoles().add(role); //配置用户表到角色表之间的关系,可以对中间表进行数据维护 1-1
// role.getUsers().add(user); //配置角色表到用户表之间的关系,可以对中间表进行数据维护 1-1
User saveUser = userRepository.save(user);
System.out.println(saveUser);
}
级联删除:
// 删除用户表中数据的时候同时删除中间表和角色表中的数据
@Test
@Transactional
@Rollback(false)
public void CascadeDeleteTest(){
User user = userRepository.getOne(1l);
userRepository.delete(user);
// 数据由用户表管理 角色表不能删除数
/* Role role = roleRepository.getOne(1l);
roleRepository.delete(role);*/
}
12.多表查询
一对多级联查询:
注意点:
如果两个实体类都重写了toString方法 会有问题 :*Error
解决方法:重写toString方法的时候,去掉关联的对象属性
延迟加载:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class MoreTableSelect {
@Autowired
private CompanyRepository companyRepository;
@Autowired
private EmployeeRepository employeeRepository;
/**
* 案例:客户和联系人
* 多表查询
* 对象导航查询:查询一个对象的同时,通过此对象查询他关联的对象
* 错误:org.hibernate.LazyInitializationException: could not initialize proxy - no Session
* 如果两个实体类都重写了toString方法 会有问题 :*Error
* 解决办法:重写toString方法的时候:去掉关联的对象属性
*/
@Test
@Transactional // 解决在java代码中的no session问题
public void objectNavigateSelectTest(){
//查询 id为 1 的公司
Company company = companyRepository.getOne(1l);
//查询公司 id为 1 下面的所有员工
List<Employee> employeeList = company.getCompanies();
for (Employee employee : employeeList) {
System.out.println(employee);
}
}
}
立即加载:
@Test
@Transactional // 解决在java代码中的no session问题
public void objectNavigateSelectGetOneTest(){
//查询 id为 1 的公司
Company company = companyRepository.findOne(1l); //立即加载
//查询公司 id为 1 下面的所有员工
List<Employee> employeeList = company.getCompanies();
for (Employee employee : employeeList) {
System.out.println(employee);
}
}
}
多对一级联查询:
// 从员工对象中查询公司对象
@Test
@Transactional // 解决在java代码中的no session问题
public void objectNavigateSelectOneToMan(){
//查询这个员工的同时,查询他所属的公司
//默认还是延迟加载:要想立即加载:@OneToMany(mappedBy = "company") 中添加fetch属性中配置立即加载
Employee employee = employeeRepository.getOne(3l);
Company company = employee.getCompany();
System.out.println(company);
}
多表查询总结:
从一方查询多方:默认使用延迟加载:
原因:因为有时候我们只想需要一方的数据,但是如果使用立即加载,就会把多方的数据全部查出来,但是又没有用到就会极其的消耗性能。所以默认使用延迟加载
从多方查询一方:默认使用立即加载:
从多方查询一方的时候,因为只有一条数据,就好比一个员工对应一个公司一样,数据比较少,不会消耗太多性能,所有采用立即加载,如果是一方查询多方采用立即加载的话,就好比一个公司有上1000个员工,但是我们有时候并不需要马上查看这1000个员工的信息,所以先创建一个代理对象,等我们正在需要查着1000个员工的信息的时候,让这个代理对象去给我们查出来就好了,这样极大的提高了我们程序运行的性能。
13、完结散花
好了,到这里就完结散花了,学完SpringDataJpa之后和Mybatis做对比的话就很清楚了,SpringDataJpa是对象关系的映射,可以逆向生成数据库表,通过操作对象来操作数据库,而Mybatis是数据结果集的映射,而Mybatis-Plus是先有数据库表然后再生成实体类和属性,两者刚刚好是相反的。
两者的优缺点:
SpringDataJpa优点:
1.SpringDataJpa是对象的关系映射,所有我们只需要操作java对象即可操作数据库。基本的一些增删改查,可以不用手写,可以更加的注重业务逻辑。
2.因为SpringDataJpa是通过java对象去操作的数据库,所有不需要关注底层数据库,更换数据库的时候,只需要更改配置文件即可,扩展性强,移植性高。
SpringDataJpa缺点:
1.因为sql都是框架底层帮我们实现的,当业务逻辑非常复杂的时候,底层框架封装的sql可能会满足不了业务需求,而且sql是封装的,在进行sql调优的时候,不太容易。
Mybatis优点:
1.因为Mybatis是数据结果集的映射,帮我们封装好了结果集,我们只需要写sql就可以,因为sql都是自己写的,在实现业务逻辑的时候,可以更加灵活。
2.sql都是完全自己写的,没有经过底层封装,所有进行sql调优的时候,非常方便。
Mybatis缺点:
1.因为是手动写的sql去匹配的数据库,所以当我们更换底层数据库的时候,sql语句也会跟着变,所以扩展性,迁移性比较差。。
2.sql是手写的,所以很多简单的增删改查都需要我们自己手动去写。
以上就是我学完SpringDataJpa和Mybatis之后的总结,对于我们技术人员来说,其实技术没有什么好坏之分,也不是谁好谁坏,即存在,即合理,看的是开发场景,不同的开发场景,可能用到的技术会不同,所有技术有时候是根据开发场景去进行匹配的。