第一步首先创建一个maven工程,导入对于的pom依赖
<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>com.cib.com</groupId> <artifactId>heimaspringdatajpa</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <!--版本锁定--> <spring.version>5.0.2.RELEASE</spring.version> <hibernate.version>5.0.7.Final</hibernate.version> <slf4j.version>1.6.6</slf4j.version> <log4j.version>1.2.12</log4j.version> <c3p0.version>0.9.1.2</c3p0.version> <mysql.version>5.1.6</mysql.version> </properties> <dependencies> <!-- junit单元测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- spring beg --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.8</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <!-- spring对orm框架的支持包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <!-- spring end --> <!-- hibernate beg --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-c3p0</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 --> <!-- log end --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <!-- log end --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <!-- spring data jpa 的坐标--> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>1.9.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <!-- el beg 使用spring data jpa 必须引入 --> <dependency> <groupId>javax.el</groupId> <artifactId>javax.el-api</artifactId> <version>2.2.4</version> </dependency> <dependency> <groupId>org.glassfish.web</groupId> <artifactId>javax.el</artifactId> <version>2.2.4</version> </dependency> <!-- el end --> </dependencies> </project>
1、接下来创建jpa的核心配置文件
jpa的核心配置文件必须放在类路径的meta-info文件夹下面,命名也必须满足对于的规则
PA规范要求在类路径的META-INF目录下放置persistence.xml, 文件的名称是固定的,配置模板如下:
<!--必须要有name属性,不能为空 -->
<persistence-unit name="myJap" transaction-type="RESOURCE_LOCAL">
这里
如果是分布式事务管理,这里就要配置为JTA,这里只操作一个数据库,这里不存在分布式事务,这里设置为RESOURCE_LOCAl
persistence.xml的内容如下所示
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <!--必须要有name属性,不能为空 --> <persistence-unit name="myJap" transaction-type="RESOURCE_LOCAL"> <!--可选 --> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <!--厂商的特定属性 --> <properties> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" /> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpa?useUnicode=true&characterEncoding=UTF-8" /> <property name="javax.persistence.jdbc.user" value="root" /> <property name="javax.persistence.jdbc.password" value="123456" /> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.hbm2ddl.auto" value="update" /> </properties> </persistence-unit> </persistence>
接下来,我们要编写实体类,创建对于的数据库表
创建客户的数据库表
drop table if exists cst_customer; /*创建客户表*/ CREATE TABLE cst_customer ( cust_id bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)', cust_name varchar(32) NOT NULL COMMENT '客户名称(公司名称)', cust_source varchar(32) DEFAULT NULL COMMENT '客户信息来源', cust_industry varchar(32) DEFAULT NULL COMMENT '客户所属行业', cust_level varchar(32) DEFAULT NULL COMMENT '客户级别', cust_address varchar(128) DEFAULT NULL COMMENT '客户联系地址', cust_phone varchar(64) DEFAULT NULL COMMENT '客户联系电话', PRIMARY KEY (`cust_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=UTF8MB4; ———————————————— 版权声明:本文为CSDN博主「底层_码农」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_40794973/article/details/98895832
注:表可以不用创建
3)创建、编写实体类和数据库表的映射配置[重点]
注解描述
@Entity:声明实体类】
@Table : 配置实体类和表的映射关系 , name : 配置数据库表的名称
/**
* @Id:声明主键的配置
* @GeneratedValue:配置主键的生成策略
* strategy
* GenerationType.IDENTITY :自增,mysql
* * 底层数据库必须支持自动增长(底层数据库支持的自动增长方式,对id自增)
* GenerationType.SEQUENCE : 序列,oracle
* * 底层数据库必须支持序列
* GenerationType.TABLE : jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
* GenerationType.AUTO : 由程序自动的帮助我们选择主键生成策略
* @Column:配置属性和字段的映射关系
* name:数据库表中字段的名称
*/
/**
* 客户编号(主键)
*/
GenerationType.IDENTITY :自增,mysql
* * 底层数据库必须支持自动增长(底层数据库支持的自动增长方式,对id自增) 必须和mysql中的 AUTO_INCREMENT一一对象,cust_id bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
@Column:配置属性和字段的映射关系
* name:数据库表中字段的名称
package com.itcast.domain; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; /** * 客户的实体类 * 配置映射关系 * * 1.实体类和表的映射关系 * @Entity:声明实体类 * @Table : 配置实体类和表的映射关系 * name : 配置数据库表的名称 * 2.实体类中属性和表中字段的映射关系 */ @Entity @Table(name = "cst_customer") public class Customer implements Serializable { /** * @Id:声明主键的配置 * @GeneratedValue:配置主键的生成策略 * strategy * GenerationType.IDENTITY :自增,mysql * * 底层数据库必须支持自动增长(底层数据库支持的自动增长方式,对id自增) * GenerationType.SEQUENCE : 序列,oracle * * 底层数据库必须支持序列 * GenerationType.TABLE : jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增 * GenerationType.AUTO : 由程序自动的帮助我们选择主键生成策略 * @Column:配置属性和字段的映射关系 * name:数据库表中字段的名称 */ /** * 客户编号(主键) */ @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; 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; } }
接下来我们就可以编写一个测试类,测试对于的jpa的操作
/**
* 测试jpa的保存
* 案例:保存一个客户到数据库中
* Jpa的操作步骤
* 1.加载配置文件创建工厂(实体管理器工厂)对象
* 2.通过实体管理器工厂获取实体管理器
* 3.获取事务对象,开启事务
* 4.完成增删改查操作
* 5.提交事务(回滚事务)
* 6.释放资源
*/
package com.itcast.cn; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import org.junit.Test; import com.itcast.domain.Customer; public class TestJpa { /** * 测试jpa的保存 * 案例:保存一个客户到数据库中 * Jpa的操作步骤 * 1.加载配置文件创建工厂(实体管理器工厂)对象 * 2.通过实体管理器工厂获取实体管理器 * 3.获取事务对象,开启事务 * 4.完成增删改查操作 * 5.提交事务(回滚事务) * 6.释放资源 */ @Test public void testSave() { //1.加载配置文件创建工厂(实体管理器工厂)对象 EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJap"); //2.通过实体管理器工厂获取实体管理器 EntityManager em = factory.createEntityManager(); //3.获取事务对象,开启事务 EntityTransaction tx = em.getTransaction(); //获取事务对象 tx.begin();//开启事务 try{ //4.完成增删改查操作:保存一个客户到数据库中 Customer customer = new Customer(); customer.setCustName("蔡徐坤"); customer.setCustIndustry("明星"); customer.setCustSource("bibi"); //保存 em.persist(customer); //保存操作 //5.提交事务 tx.commit(); }catch (Exception e){ tx.rollback();//回滚事务 e.printStackTrace(); }finally { //6.释放资源 em.close(); //关闭工厂 factory.close(); } } }
运行的sql语句如下所示
log4j:WARN No appenders could be found for logger (org.jboss.logging).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source) values (?, ?, ?, ?, ?, ?)
GenerationType.TABLE : jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增,jpa会另外生成一张表帮助我们进行主键的管理
GenerationType.AUTO : 由程序自动的帮助我们选择主键生成策略
多个线程在内存中共享一份Factory对象,减少Factory对象创建消耗资源
采用静态代码块的方法,减少Factory资源创建消耗性能的问题
1. 抽取JPAUtil工具类
package com.itcast.utils; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; /** * 解决实体管理器工厂的浪费资源和耗时问题 * 通过静态代码块的形式,当程序第一次访问此工具类时,创建一个公共的实体管理器工厂对象 * * 第一次访问getEntityManager方法:经过静态代码块创建一个factory对象,再调用方法创建一个EntityManager对象 * 第二次方法getEntityManager方法:直接通过一个已经创建好的factory对象,创建EntityManager对象 */ public class JpaUtils { private static EntityManagerFactory factory; static { //1.加载配置文件,创建entityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa"); } /** * 获取EntityManager对象 */ public static EntityManager getEntityManager() { return factory.createEntityManager(); } }
我们在编写一个测试类进行测试
解决创建 EntityManagerFactory 浪费资源问题
3. 测试查询
查询有两个方法,注意区别
package com.itcast.cn; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import org.junit.Test; import com.itcast.domain.Customer; import com.itcast.utils.JpaUtils; public class TestJpa { /** * 测试jpa的保存 * 案例:保存一个客户到数据库中 * Jpa的操作步骤 * 1.加载配置文件创建工厂(实体管理器工厂)对象 * 2.通过实体管理器工厂获取实体管理器 * 3.获取事务对象,开启事务 * 4.完成增删改查操作 * 5.提交事务(回滚事务) * 6.释放资源 */ @Test public void testSave() { //1.加载配置文件创建工厂(实体管理器工厂)对象 EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJap"); //2.通过实体管理器工厂获取实体管理器 EntityManager em = factory.createEntityManager(); //3.获取事务对象,开启事务 EntityTransaction tx = em.getTransaction(); //获取事务对象 tx.begin();//开启事务 try{ //4.完成增删改查操作:保存一个客户到数据库中 Customer customer = new Customer(); customer.setCustName("蔡徐坤"); customer.setCustIndustry("明星"); customer.setCustSource("bibi"); //保存 em.persist(customer); //保存操作 //5.提交事务 tx.commit(); }catch (Exception e){ tx.rollback();//回滚事务 e.printStackTrace(); }finally { //6.释放资源 em.close(); //关闭工厂 factory.close(); } } @Test public void testtestSave02(){ //1.获取工厂(实体管理器工厂)对象 EntityManager em = JpaUtils.getEntityManager(); //3.获取事务对象,开启事务 EntityTransaction tx = em.getTransaction(); //获取事务对象 tx.begin();//开启事务 //4.完成增删改查操作:保存一个客户到数据库中 Customer customer = new Customer(); customer.setCustName("蔡徐坤"); customer.setCustIndustry("明星"); customer.setCustSource("bibi"); //保存 em.persist(customer); //保存操作 //5.提交事务 tx.commit(); //6.释放资源 em.close(); } /** * 根据id查询客户 * 使用find方法查询: * 1.查询的对象就是当前客户对象本身 * 2.在调用find方法的时候,就会发送sql语句查询数据库 * * 立即加载 */ @Test public void testFind() { //1.通过工具类获取entityManager EntityManager entityManager = JpaUtils.getEntityManager(); //3.增删改查 -- 根据id查询客户 /** * find : 根据id查询数据 * class:查询数据的结果需要包装的实体类类型的字节码 * id:查询的主键的取值 * 没有返回 null */ Customer customer = entityManager.find(Customer.class, 1L);//打断点 //不管打不打印(使用),执行到上一条语句时,都会发送SQL语句 //System.out.print(customer); //class com.bug.domain.Customer System.out.println(customer.toString()); //5.释放资源 entityManager.close(); } /** * 根据id查询客户 * getReference方法 * 1.获取的对象是一个动态代理对象 * 2.调用getReference方法不会立即发送sql语句查询数据库 * * 当调用查询结果对象的时候,才会发送查询的sql语句:什么时候用,什么时候发送sql语句查询数据库 * * 延迟加载(懒加载) * * 得到的是一个动态代理对象 * * 什么时候用,什么使用才会查询 */ @Test public void testReference() { //1.通过工具类获取entityManager EntityManager entityManager = JpaUtils.getEntityManager(); //3.增删改查 -- 根据id查询客户 /** * getReference : 根据id查询数据 * class:查询数据的结果需要包装的实体类类型的字节码 * id:查询的主键的取值 */ Customer customer = entityManager.getReference(Customer.class, 1L);//打断点 //这里不打印(不使用)就不会发送SQL语句 //System.out.print(customer); //class com.bug.domain.Customer_$$_jvst88e_0 代理对象 System.out.println(" "); System.out.println(customer.toString()); //5.释放资源 entityManager.close(); } }
//3.增删改查 -- 根据id查询客户
/**
* getReference : 根据id查询数据
* class:查询数据的结果需要包装的实体类类型的字节码
* id:查询的主键的取值
*/
getReference 是懒加载,在find查询语句的时候不会发起sql语句查询,在实际使用使用System.out.print(customer)的时候才发起sql语句进行查询,返回的查询结果是一个代理对象
4. 测试删除
@Test public void testRemove() { //1.通过工具类获取entityManager EntityManager entityManager = JpaUtils.getEntityManager(); //3.增删改查 -- 删除客户 //3.获取事务对象,开启事务 EntityTransaction tx = entityManager.getTransaction(); //获取事务对象 tx.begin();//开启事务 //i 根据id查询客户 Customer customer = entityManager.find(Customer.class, 1L); System.out.println(customer); if(customer != null){ //ii 调用remove方法完成删除操作 entityManager.remove(customer); } //5.提交事务 tx.commit(); //5.释放资源 entityManager.close(); }
7.JPA中的复杂查询
JPQL全称Java Persistence Query Language,Java持久化查询语言(JPQL),它是一种可移植的查询语言,旨在以面向对象表达式语言的表达式,将SQL语法和简单查询语义绑定在一起·使用这种语言编写的查询是可移植的,可以被编译成所有主流数据库服务器上的SQL。
其特征与原生SQL语句类似,并且完全面向对象,通过类名和属性访问,而不是表名和表的属性。
jpql语句不能写select *,但是能够写select 加上对象的属性值
查询数据库 表中存在多少条记录
//统计查询 @Test public void findCount() { EntityManager em = null; EntityTransaction tx = null; try { //获取实体管理对象 em = JPAUtil.getEntityManager(); //获取事务对象 tx = em.getTransaction(); tx.begin(); // 查询全部客户 // 1.创建query对象 String jpql = "select count(custId) from Customer"; Query query = em.createQuery(jpql); // 2.查询并得到返回结果 Object count = query.getSingleResult(); // 得到集合返回类型 System.out.println(count); tx.commit(); } catch (Exception e) { // 回滚事务 tx.rollback(); e.printStackTrace(); } finally { // 释放资源 em.close(); } } ———————————————— 版权声明:本文为CSDN博主「I Java」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/zlx_508/article/details/97005499
记录总数只有一条记录,使用getSingleResult,返回一条记录
分页查询
0表示查询第一页,2表示第一页显示2条记录
//分页查询客户 @Test public void findPaged () { EntityManager em = null; EntityTransaction tx = null; try { //获取实体管理对象 em = JPAUtil.getEntityManager(); //获取事务对象 tx = em.getTransaction(); tx.begin(); //创建query对象 String jpql = "from Customer"; Query query = em.createQuery(jpql); //起始索引 query.setFirstResult(0); //每页显示条数 query.setMaxResults(2); //查询并得到返回结果 List list = query.getResultList(); //得到集合返回类型 for (Object object : list) { System.out.println(object); } tx.commit(); } catch (Exception e) { // 回滚事务 tx.rollback(); e.printStackTrace(); } finally { // 释放资源 em.close(); } } ———————————————— 版权声明:本文为CSDN博主「I Java」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/zlx_508/article/details/97005499
接下来我们讲解条件查询
//条件查询 @Test public void findCondition () { EntityManager em = null; EntityTransaction tx = null; try { //获取实体管理对象 em = JPAUtil.getEntityManager(); //获取事务对象 tx = em.getTransaction(); tx.begin(); //创建query对象 String jpql = "from Customer where custName like ? "; Query query = em.createQuery(jpql); //对占位符赋值,从1开始 query.setParameter(1, "传智播客%"); //查询并得到返回结果 Object object = query.getSingleResult(); //得到唯一的结果集对象 System.out.println(object); tx.commit(); } catch (Exception e) { // 回滚事务 tx.rollback(); e.printStackTrace(); } finally { // 释放资源 em.close(); } } ———————————————— 版权声明:本文为CSDN博主「I Java」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/zlx_508/article/details/97005499
接下来重点讲解spring -data-jpa
这里配置文件有点问题,不清楚的看佟刚spring data的视频教程
pom.xml的依赖如下
<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>com.cib.com</groupId> <artifactId>heimaspringdatajpa</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <!--版本锁定--> <spring.version>5.0.2.RELEASE</spring.version> <hibernate.version>5.0.7.Final</hibernate.version> <slf4j.version>1.6.6</slf4j.version> <log4j.version>1.2.12</log4j.version> <c3p0.version>0.9.1.2</c3p0.version> <mysql.version>5.1.6</mysql.version> </properties> <dependencies> <!-- junit单元测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- spring beg --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.8</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <!-- spring对orm框架的支持包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <!-- spring end --> <!-- hibernate beg --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-c3p0</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 --> <!-- log end --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <!-- log end --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <!-- spring data jpa 的坐标--> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>1.9.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <!-- el beg 使用spring data jpa 必须引入 --> <dependency> <groupId>javax.el</groupId> <artifactId>javax.el-api</artifactId> <version>2.2.4</version> </dependency> <dependency> <groupId>org.glassfish.web</groupId> <artifactId>javax.el</artifactId> <version>2.2.4</version> </dependency> <!-- el end --> </dependencies> </project>
db.properties的配置如下
jdbc.user=root jdbc.password=123456 jdbc.driverClass=com.mysql.jdbc.Driver jdbc.jdbcUrl=jdbc:mysql:///jpa
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 配置自动扫描的包 --> <context:component-scan base-package="com.atguigu.springdata"></context:component-scan> <!-- 1. 配置数据源 --> <context:property-placeholder location="classpath:db.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <!-- 配置其他属性 --> </bean> <!-- 2. 配置 JPA 的 EntityManagerFactory --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean> </property> <property name="packagesToScan" value="com.atguigu.springdata"></property> <property name="jpaProperties"> <props> <!-- 二级缓存相关 --> <!-- <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop> <prop key="net.sf.ehcache.configurationResourceName">ehcache-hibernate.xml</prop> --> <!-- 生成的数据表的列的映射策略 --> <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop> <!-- hibernate 基本属性 --> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> </bean> <!-- 3. 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"></property> </bean> <!-- 4. 配置支持注解的事务 --> <tx:annotation-driven transaction-manager="transactionManager"/> <!-- 5. 配置 SpringData --> <!-- 加入 jpa 的命名空间 --> <!-- base-package: 扫描 Repository Bean 所在的 package --> <jpa:repositories base-package="com.atguigu.springdata" entity-manager-factory-ref="entityManagerFactory"></jpa:repositories> </beans>
这里有几个配置很关键
<jpa:repositories base-package="com.atguigu.springdata" 指定扫描@Reporisty注解的包
<property name="packagesToScan" value="com.atguigu.springdata"></property>指定@Entiry注解的包
<context:component-scan base-package="com.atguigu.springdata"></context:component-scan>指定spring @service @compent所在的包路径
接下来我们创建包com.atguigu.springdata,所有在的操作都在这个包下面
接下来我们创建实体类
package com.atguigu.springdata; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity // 声明此类是个实体类 需要导入javax.persistence.Entity; @Table(name = "cst_customer") //需要将该类对应到数据库中的哪一个表中,name中填表的名称 public class Customer { @Id //声明主键 @GeneratedValue(strategy = GenerationType.IDENTITY) //声明主键的生成策略为自动递增 //mysql使用自增模式,orcle 使用序列模式 @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 cusIndustry; @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 getCusIndustry() { return cusIndustry; } public void setCusIndustry(String cusIndustry) { this.cusIndustry = cusIndustry; } public String getCustPhone() { return custPhone; } public void setCustPhone(String custPhone) { this.custPhone = custPhone; } public String getCustAddress() { return custAddress; } public void setCustAddress(String custAddress) { this.custAddress = custAddress; } @Override public String toString() { return "Customer [custId=" + custId + ", custName=" + custName + ", custSource=" + custSource + ", custLevel=" + custLevel + ", cusIndustry=" + cusIndustry + ", custPhone=" + custPhone + ", custAddress=" + custAddress + "]"; } }
实体类创建成功之后,接下来我们就可以编写jpa的接口进行增删改查的操作了,只需要编写jpa接口,不需要编写jpa的实现类
关键点:
1.编写dao层的接口就可以了,不需要dao接口的实现类
2、编写的dao接口的规范如下
SpringDataJPA通过实现接口(代理的形式)进行简单的CRUD
https://blog.csdn.net/qq_42041712/article/details/94451572
JpaRepository<Customer,Long> 第一个参数是我们要操作的实体类类型,第二个参数是实体类的主键
JpaSpecificationExecutor<Customer> 参数是我们要操作的实体类的类型
package com.atguigu.springdata; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /* * 符合SpringDatajpa的dao层接口规范 *JpaRepository<操作的实体类类型,实体类中主键属性的类型> *封装了基本的CRUD操作 JpaSpecificationExecutor<操作的实体类类型> *封装了复杂查询操作(分页) * */ public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> { }
接下来编写好接口之后,就可以进行增加删除操作数据库了,注意CustomerDao没有使用注解,只要放在<jpa:repositories base-package="com.atguigu.springdata"对于的包下面就可以了
我们可以编写一个测试类进行操作,首先保证数据库中存在数据
import java.io.FileNotFoundException; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.stream.Collector; import java.util.stream.Collectors; import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.sql.DataSource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.atguigu.springdata.CustomerDao; import com.itcast.domain.Customer; import com.itcast.utils.JpaUtils; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext.xml") public class SpringDataTest { @Autowired private CustomerDao dao; @Test public void testFind() { com.atguigu.springdata.Customer customer = dao.findOne((long) 2); System.out.println(customer.toString()); } }
测试的结果如下
在eclipse中查看一个类及其父类中的所有方法和属性
只需要连续按两次Ctrl+O
JpaRepository默认实现了下面的方法
在eclipse中查看一个类的集成关系
只需要连续按两次Ctrl+T
JpaRepository继承自PagingAndSortingRepository,PagingAndSortingRepository继承自CrudRepository,CrudRepository集成自Repository
import java.io.FileNotFoundException; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.stream.Collector; import java.util.stream.Collectors; import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.sql.DataSource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.atguigu.springdata.Customer; import com.atguigu.springdata.CustomerDao; import com.itcast.utils.JpaUtils; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext.xml") public class SpringDataTest { @Autowired private CustomerDao customerDao; @Test public void testFind() { com.atguigu.springdata.Customer customer = customerDao.findOne((long) 2); System.out.println(customer.toString()); } /** * 保存客户:调用save(obj)方法 */ @Test public void testSave() { Customer c = new Customer(); c.setCustName("传智播客"); customerDao.save(c); } /** * 修改客户:调用save(obj)方法 * 对于save方法的解释:如果执行此方法是对象中存在id属性,即为更新操作会先根据id查询,再更新 * 如果执行此方法中对象中不存在id属性,即为保存操作 * */ @Test public void testUpdate() { //根据id查询id为1的客户 Customer customer = customerDao.findOne(1l); //修改客户名称 customer.setCustName("传智播客顺义校区"); //更新 customerDao.save(customer); } }
/**
* 根据id删除:调用delete(id)方法
*/
@Test
public void testDelete() {
customerDao.delete(1l);
}
import java.io.FileNotFoundException; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.stream.Collector; import java.util.stream.Collectors; import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.sql.DataSource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.atguigu.springdata.Customer; import com.atguigu.springdata.CustomerDao; import com.itcast.utils.JpaUtils; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext.xml") public class SpringDataTest { @Autowired private CustomerDao customerDao; @Test public void testFind() { com.atguigu.springdata.Customer customer = customerDao.findOne((long) 2); System.out.println(customer.toString()); } /** * 保存客户:调用save(obj)方法 */ @Test public void testSave() { Customer c = new Customer(); c.setCustName("传智播客"); customerDao.save(c); } /** * 修改客户:调用save(obj)方法 * 对于save方法的解释:如果执行此方法是对象中存在id属性,即为更新操作会先根据id查询,再更新 * 如果执行此方法中对象中不存在id属性,即为保存操作 * */ @Test public void testUpdate() { //根据id查询id为1的客户 Customer customer = customerDao.findOne(1l); //修改客户名称 customer.setCustName("传智播客顺义校区"); //更新 customerDao.save(customer); } @Test public void testCount() { long count = customerDao.count(); System.out.println(count); } @Test public void testExits() { boolean exists = customerDao.exists((long) 2); System.out.println(exists); } }
@Test @Transactional public void testGetOne() { Customer one = customerDao.getOne((long) 2); System.out.println(one); }
getOne是懒加载,findOne是直接加载,懒加载的实现需要依赖事务,所有使用getOne方法的时候,一定要引入事务管理的依赖,这里使用 @Transactiona,否则代码会报错
3.2 使用JPQL的方式查询
使用Spring Data JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用@Query注解,结合JPQL的语句方式完成查询
package com.atguigu.springdata; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import antlr.collections.List; /* * 符合SpringDatajpa的dao层接口规范 *JpaRepository<操作的实体类类型,实体类中主键属性的类型> *封装了基本的CRUD操作 JpaSpecificationExecutor<操作的实体类类型> *封装了复杂查询操作(分页) * */ public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> { //@Query 使用jpql的方式查询。?1代表参数的占位符,其中1对应方法中的参数索引 @Query(value="from Customer where custName = ?1") public Customer findCustomer(String custName); }
//@Query 使用jpql的方式查询。?1代表参数的占位符,其中1对应方法中的参数索引
2表示的是输入参数为name,1表示输入的参数是id
此外,也可以通过使用 @Query 来执行一个更新操作,为此,我们需要在使用 @Query 的同时,用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询
package com.atguigu.springdata; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import antlr.collections.List; /* * 符合SpringDatajpa的dao层接口规范 *JpaRepository<操作的实体类类型,实体类中主键属性的类型> *封装了基本的CRUD操作 JpaSpecificationExecutor<操作的实体类类型> *封装了复杂查询操作(分页) * */ public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> { //@Query 使用jpql的方式查询。?1代表参数的占位符,其中1对应方法中的参数索引 @Query(value="from Customer where custName = ?1") public Customer findCustomer(String custName); @Query(value="update Customer set custName = ?1 where custId = ?2") @Modifying public void updateCustomer(String custName,Long custId); }
执行更新或者删除操作,必须需要添加 @Modifying,此外必须要具有事务操作
@Test @Transactional @Rollback(value=false) public void testupdateCustomer() { customerDao.updateCustomer("我是可不2222", (long) 2); }
执行更新删除操作需要有事务所有上面有@Transactional,此外jpa执行事务操作完成之后默认会回滚,当数据更新操作成功之后,因为jpa默认要回滚,会把更新的数据还原回去,我们要在数据库中看到更新的
数据,我们要禁止jpa的回滚@Rollback(value=false)
3.3 使用SQL语句查询
Spring Data JPA同样也支持sql语句的查询,如下:
package com.atguigu.springdata; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; /* * 符合SpringDatajpa的dao层接口规范 *JpaRepository<操作的实体类类型,实体类中主键属性的类型> *封装了基本的CRUD操作 JpaSpecificationExecutor<操作的实体类类型> *封装了复杂查询操作(分页) * */ public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> { //@Query 使用jpql的方式查询。?1代表参数的占位符,其中1对应方法中的参数索引 @Query(value="from Customer where custName = ?1") public Customer findCustomer(String custName); @Query(value="update Customer set custName = ?1 where custId = ?2") @Modifying public void updateCustomer(String custName,Long custId); /** * nativeQuery : 使用本地sql的方式查询 */ @Query(value="select * from cst_customer",nativeQuery=true) public List<Object[]> findSql(); }
这里千万要注意返回的是一个List<Object[]>,每一个元素是一个Object类型的数组,不能写成List<Customer[]>
@Test public void testfindSql() { List<Object[]> datas = customerDao.findSql(); for(Object[] data:datas){ System.out.println(Arrays.toString(data)); } }
package com.atguigu.springdata; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; /* * 符合SpringDatajpa的dao层接口规范 *JpaRepository<操作的实体类类型,实体类中主键属性的类型> *封装了基本的CRUD操作 JpaSpecificationExecutor<操作的实体类类型> *封装了复杂查询操作(分页) * */ public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> { //@Query 使用jpql的方式查询。?1代表参数的占位符,其中1对应方法中的参数索引 @Query(value="from Customer where custName = ?1") public Customer findCustomer(String custName); @Query(value="update Customer set custName = ?1 where custId = ?2") @Modifying public void updateCustomer(String custName,Long custId); /** * nativeQuery : 使用本地sql的方式查询 */ @Query(value="select * from cst_customer",nativeQuery=true) public List<Object[]> findSql(); @Query(value = "select * from cst_customer where cust_name like ?1",nativeQuery = true) public List<Object []> findSql2(String name);
}
测试代码
import java.io.FileNotFoundException; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.stream.Collector; import java.util.stream.Collectors; import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.sql.DataSource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.Query; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; import com.atguigu.springdata.Customer; import com.atguigu.springdata.CustomerDao; import com.itcast.utils.JpaUtils; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext.xml") public class SpringDataTest { @Autowired private CustomerDao customerDao; @Test public void testFind() { com.atguigu.springdata.Customer customer = customerDao.findOne((long) 2); System.out.println(customer.toString()); } /** * 保存客户:调用save(obj)方法 */ @Test public void testSave() { Customer c = new Customer(); c.setCustName("传智播客"); customerDao.save(c); } /** * 修改客户:调用save(obj)方法 * 对于save方法的解释:如果执行此方法是对象中存在id属性,即为更新操作会先根据id查询,再更新 * 如果执行此方法中对象中不存在id属性,即为保存操作 * */ @Test public void testUpdate() { //根据id查询id为1的客户 Customer customer = customerDao.findOne(1l); //修改客户名称 customer.setCustName("传智播客顺义校区"); //更新 customerDao.save(customer); } @Test public void testCount() { long count = customerDao.count(); System.out.println(count); } @Test public void testExits() { boolean exists = customerDao.exists((long) 2); System.out.println(exists); } @Test @Transactional public void testGetOne() { Customer one = customerDao.getOne((long) 2); System.out.println(one); } @Test @Transactional @Rollback(value=false) public void testupdateCustomer() { customerDao.updateCustomer("我是可不2222", (long) 2); } @Test public void testfindSql() { List<Object[]> datas = customerDao.findSql(); for(Object[] data:datas){ System.out.println(Arrays.toString(data)); } } //测试sql查询 @Test public void testFindSql() { List<Object[]> list = customerDao.findSql2("迅腾软件%");//模糊查询 for(Object[] obj: list){ System.out.println(Arrays.toString(obj)); } } }
3.4 方法命名规则查询
顾名思义,方法命名规则查询就是根据方法的名字,就能创建查询。只需要按照Spring Data JPA提供的方法命名规则定义方法的名称,就可以完成查询工作。Spring Data JPA在程序执行的时候会根据方法名称进行解析,并自动生成查询语句进行查询
按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。
package com.atguigu.springdata; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; /* * 符合SpringDatajpa的dao层接口规范 *JpaRepository<操作的实体类类型,实体类中主键属性的类型> *封装了基本的CRUD操作 JpaSpecificationExecutor<操作的实体类类型> *封装了复杂查询操作(分页) * */ public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> { //@Query 使用jpql的方式查询。?1代表参数的占位符,其中1对应方法中的参数索引 @Query(value="from Customer where custName = ?1") public Customer findCustomer(String custName); @Query(value="update Customer set custName = ?1 where custId = ?2") @Modifying public void updateCustomer(String custName,Long custId); /** * nativeQuery : 使用本地sql的方式查询 */ @Query(value="select * from cst_customer",nativeQuery=true) public List<Object[]> findSql(); @Query(value = "select * from cst_customer where cust_name like ?1",nativeQuery = true) public List<Object []> findSql2(String name); //方法命名方式查询(根据客户名称查询客户) public Customer findByCustName(String custName); }
测试代码如下
@Test public void testFindSql22() { Customer customer = customerDao.findByCustName("3333"); System.out.println(customer.toString()); }
我们使用下Like查看下模糊查询
package com.atguigu.springdata; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; /* * 符合SpringDatajpa的dao层接口规范 *JpaRepository<操作的实体类类型,实体类中主键属性的类型> *封装了基本的CRUD操作 JpaSpecificationExecutor<操作的实体类类型> *封装了复杂查询操作(分页) * */ public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> { //@Query 使用jpql的方式查询。?1代表参数的占位符,其中1对应方法中的参数索引 @Query(value="from Customer where custName = ?1") public Customer findCustomer(String custName); @Query(value="update Customer set custName = ?1 where custId = ?2") @Modifying public void updateCustomer(String custName,Long custId); /** * nativeQuery : 使用本地sql的方式查询 */ @Query(value="select * from cst_customer",nativeQuery=true) public List<Object[]> findSql(); @Query(value = "select * from cst_customer where cust_name like ?1",nativeQuery = true) public List<Object []> findSql2(String name); //方法命名方式查询(根据客户名称查询客户) public Customer findByCustName(String custName); public List<Customer> findByCustNameLike(String custName); }
测试代码如下
@Test public void testFindSql22() { List<Customer> customers = customerDao.findByCustNameLike("3333%"); for(Customer customer:customers){ System.out.println(customer.toString()); } }
这里没有占位符,两个参数顺利不能弄错了
package com.atguigu.springdata; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; /* * 符合SpringDatajpa的dao层接口规范 *JpaRepository<操作的实体类类型,实体类中主键属性的类型> *封装了基本的CRUD操作 JpaSpecificationExecutor<操作的实体类类型> *封装了复杂查询操作(分页) * */ public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> { //@Query 使用jpql的方式查询。?1代表参数的占位符,其中1对应方法中的参数索引 @Query(value="from Customer where custName = ?1") public Customer findCustomer(String custName); @Query(value="update Customer set custName = ?1 where custId = ?2") @Modifying public void updateCustomer(String custName,Long custId); /** * nativeQuery : 使用本地sql的方式查询 */ @Query(value="select * from cst_customer",nativeQuery=true) public List<Object[]> findSql(); @Query(value = "select * from cst_customer where cust_name like ?1",nativeQuery = true) public List<Object []> findSql2(String name); //方法命名方式查询(根据客户名称查询客户) public Customer findByCustName(String custName); public List<Customer> findByCustNameLike(String custName); public List<Customer> findByCustNameLikeAndCusIndustry(String custName,String cusIndustry); }
测试代码如下
@Test public void testFindSql22() { List<Customer> customers = customerDao.findByCustNameLikeAndCusIndustry("3333%","明星"); for(Customer customer:customers){ System.out.println(customer.toString()); } } Hibernate: select customer0_.cust_id as cust_id1_0_, customer0_.cust_industry as cust_ind2_0_, customer0_.cust_address as cust_add3_0_, customer0_.cust_level as cust_lev4_0_, customer0_.cust_name as cust_nam5_0_, customer0_.cust_phone as cust_pho6_0_, customer0_.cust_source as cust_sou7_0_ from cst_customer customer0_ where ( customer0_.cust_name like ? ) and customer0_.cust_industry=?
我们重点来看下JpaSpecificationExecutor方法
这个接口有上面的几个方法,T findOne(Specification<T> spec);之前的JpaRepository接口也有findOne方法只能依据主键查询,但是没有携带Specification参数,这里携带了Specification参数
我们可以按照主键查询,也可以安装名称查询,我们可以自定义查询条件
/** * Specification in the sense of Domain Driven Design. * * @author Oliver Gierke * @author Thomas Darimont */ public interface Specification<T> { /** * Creates a WHERE clause for a query of the referenced entity in form of a {@link Predicate} for the given * {@link Root} and {@link CriteriaQuery}. * * @param root * @param query * @return a {@link Predicate}, must not be {@literal null}. */ Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb); }
我们来测试下上面的几个接口
对象CriteriaBuilder有下面这样多的比较的方式
@Test public void testFindSql223333() { Specification<Customer> spec = new Specification<Customer>() { public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) { // TODO Auto-generated method stub Path<Object> custName = root.get("custName"); Predicate predicate = cb.equal(custName, "3333"); return predicate; } }; Customer customer = customerDao.findOne(spec); System.out.println(customer.toString()); }
多个查询条件的拼接
@Test public void testFindSql223333222() { Specification<Customer> spec = new Specification<Customer>() { public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) { // TODO Auto-generated method stub Path<Object> custName = root.get("custName"); Path<Object> cusIndustry = root.get("cusIndustry"); Predicate predicate1 = cb.equal(custName, "3333"); Predicate predicate2 = cb.equal(cusIndustry, "2222"); Predicate predicate3 = cb.and(predicate1,predicate2); return predicate3; } }; Customer customer = customerDao.findOne(spec); System.out.println(customer.toString()); }
@Test public void testFindSql223333222wwww() { Specification<Customer> spec = new Specification<Customer>() { public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) { // TODO Auto-generated method stub Path<Object> custName = root.get("custName"); Predicate predicate = cb.like(custName.as(String.class), "33%"); return predicate; } }; List<Customer> customer = customerDao.findAll(spec); System.out.println(customer.toString()); }
对于 List<Customer> customer = customerDao.findAll(spec)返回的数据是一个集合,我们可以指定返回的集合中的数据按照某种方式进行排序
@Test public void testFindSql223333222wwwwww() { Specification<Customer> spec = new Specification<Customer>() { public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) { // TODO Auto-generated method stub Path<Object> custName = root.get("custName"); Predicate predicate = cb.like(custName.as(String.class), "33%"); return predicate; } }; Sort sort = new Sort(Sort.Direction.ASC,"custId"); List<Customer> customer = customerDao.findAll(spec,sort); System.out.println(customer.toString()); }
按照"custId"的Sort.Direction.ASC进行排序
/** * 目标: 实现带查询条件的分页. id > 5 的条件 * * 调用 JpaSpecificationExecutor 的 Page<T> findAll(Specification<T> spec, Pageable pageable); * Specification: 封装了 JPA Criteria 查询的查询条件 * Pageable: 封装了请求分页的信息: 例如 pageNo, pageSize, Sort */ @Test public void testJpaSpecificationExecutorss(){ int pageNo = 3 - 1; int pageSize = 5; Sort sort = new Sort(Sort.Direction.ASC,"custId"); //封装分页的信息 PageRequest pageable = new PageRequest(pageNo, pageSize,sort); Specification<Customer> spec = new Specification<Customer>() { public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) { // TODO Auto-generated method stub Path<Object> custName = root.get("custName"); Predicate predicate = cb.like(custName.as(String.class), "33%"); return predicate; } }; Page<Customer> page = customerDao.findAll(spec, pageable); System.out.println("总记录数: " + page.getTotalElements()); System.out.println("当前第几页: " + (page.getNumber() + 1)); System.out.println("总页数: " + page.getTotalPages()); System.out.println("当前页面的 List: " + page.getContent()); List<Customer> content = page.getContent(); System.out.println("当前页面的记录数: " + page.getNumberOfElements()); /*List<Person> collect = page.getContent().stream().map(stat->(Person)stat).collect(Collectors.toList()); System.out.println("当前页面的 List1: " + collect);*/ }
打印结果如下
Hibernate: select count(customer0_.cust_id) as col_0_0_ from cst_customer customer0_ where customer0_.cust_name like ? 总记录数: 1 当前第几页: 3 总页数: 1 当前页面的 List: [] 当前页面的记录数: 0
客户个联系人是一对多的关系
3.2 实战Hibernate一对多关联映射 3.2.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_phone` varchar(64) DEFAULT NULL COMMENT '固定电话', `cust_mobile` varchar(16) DEFAULT NULL COMMENT '移动电话', PRIMARY KEY (`cust_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; 联系人表: CREATE TABLE `cst_linkman` ( `lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)', `lkm_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名', `lkm_cust_id` bigint(32) DEFAULT NULL COMMENT '客户id', `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_qq` varchar(16) DEFAULT NULL COMMENT '联系人qq', `lkm_position` varchar(16) DEFAULT NULL COMMENT '联系人职位', `lkm_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注', 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=1 DEFAULT CHARSET=utf8;
接下来我们要新增一个联系人的实体类对象
在customer客户端实体类中,需要配置下面的信息
package com.atguigu.springdata; import java.util.HashSet; import java.util.Set; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToMany; import javax.persistence.Table; @Entity // 声明此类是个实体类 需要导入javax.persistence.Entity; @Table(name = "cst_customer") //需要将该类对应到数据库中的哪一个表中,name中填表的名称 public class Customer { @Id //声明主键 @GeneratedValue(strategy = GenerationType.IDENTITY) //声明主键的生成策略为自动递增 //mysql使用自增模式,orcle 使用序列模式 @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 cusIndustry; @Column(name = "cust_phone") private String custPhone; @Column(name = "cust_address") private String custAddress; @OneToMany(targetEntity=LinkMan.class) @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id") private Set<LinkMan> linkMans = new HashSet<LinkMan>(); 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 getCusIndustry() { return cusIndustry; } public void setCusIndustry(String cusIndustry) { this.cusIndustry = cusIndustry; } public String getCustPhone() { return custPhone; } public void setCustPhone(String custPhone) { this.custPhone = custPhone; } public String getCustAddress() { return custAddress; } public void setCustAddress(String custAddress) { this.custAddress = custAddress; } @Override public String toString() { return "Customer [custId=" + custId + ", custName=" + custName + ", custSource=" + custSource + ", custLevel=" + custLevel + ", cusIndustry=" + cusIndustry + ", custPhone=" + custPhone + ", custAddress=" + custAddress + "]"; } public Set<LinkMan> getLinkMans() { return linkMans; } public void setLinkMans(Set<LinkMan> linkMans) { this.linkMans = linkMans; } }
在联系人方需要配置如下
package com.atguigu.springdata; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.Table; @Entity // 声明此类是个实体类 需要导入javax.persistence.Entity; @Table(name = "cst_linkman") //需要将该类对应到数据库中的哪一个表中,name中填表的名称 public class LinkMan {
@Id //声明主键
@GeneratedValue(strategy = GenerationType.IDENTITY) //声明主键的生成策略为自动递增
@Column(name = "lkm_id") //设置该属性和数据库中的哪一个字段对应
private Long lkmId; @Column(name = "lkm_gender") //设置该属性和数据库中的哪一个字段对应 private Character lkmGender; @Column(name = "lkm_name") //设置该属性和数据库中的哪一个字段对应 private String lkmName; @Column(name = "lkm_phone") //设置该属性和数据库中的哪一个字段对应 private String lkmPhone; @Column(name = "lkm_email") //设置该属性和数据库中的哪一个字段对应 private String lkmEmail; @Column(name = "lkm_qq") //设置该属性和数据库中的哪一个字段对应 private String lkmQq; @Column(name = "lkm_mobile") //设置该属性和数据库中的哪一个字段对应 private String lkmMobile; @Column(name = "lkm_memo") //设置该属性和数据库中的哪一个字段对应 private String lkmMemo; @Column(name = "lkm_position") //设置该属性和数据库中的哪一个字段对应 private String lkmPosition; @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; } public Long getLkmId() { return lkmId; } public void setLkmId(Long lkmId) { this.lkmId = lkmId; } public Character getLkmGender() { return lkmGender; } public void setLkmGender(Character lkmGender) { this.lkmGender = lkmGender; } public String getLkmName() { return lkmName; } public void setLkmName(String lkmName) { this.lkmName = lkmName; } public String getLkmPhone() { return lkmPhone; } public void setLkmPhone(String lkmPhone) { this.lkmPhone = lkmPhone; } public String getLkmEmail() { return lkmEmail; } public void setLkmEmail(String lkmEmail) { this.lkmEmail = lkmEmail; } public String getLkmQq() { return lkmQq; } public void setLkmQq(String lkmQq) { this.lkmQq = lkmQq; } public String getLkmMobile() { return lkmMobile; } public void setLkmMobile(String lkmMobile) { this.lkmMobile = lkmMobile; } public String getLkmMemo() { return lkmMemo; } public void setLkmMemo(String lkmMemo) { this.lkmMemo = lkmMemo; } public String getLkmPosition() { return lkmPosition; } public void setLkmPosition(String lkmPosition) { this.lkmPosition = lkmPosition; } }
@JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")中表示的是表联系人中的lkm_cust_id字段的值,来自于表客户的cust_id
接下来,我们要编写一个操作联系人的接口类
package com.atguigu.springdata; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; /* * 符合SpringDatajpa的dao层接口规范 *JpaRepository<操作的实体类类型,实体类中主键属性的类型> *封装了基本的CRUD操作 JpaSpecificationExecutor<操作的实体类类型> *封装了复杂查询操作(分页) * */ public interface LinnkManDao extends JpaRepository<LinkMan,Long>, JpaSpecificationExecutor<LinkMan> { }
整个工程的结构如下所示
接下来我们来就可以进行测试了
我们来看下程序的代码
import java.io.FileNotFoundException; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.stream.Collector; import java.util.stream.Collectors; import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.sql.DataSource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.Query; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; import com.atguigu.springdata.Customer; import com.atguigu.springdata.CustomerDao; import com.atguigu.springdata.LinkMan; import com.atguigu.springdata.LinnkManDao; import com.itcast.utils.JpaUtils; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext.xml") public class oneToManyTest { @Autowired private CustomerDao customerDao; @Autowired private LinnkManDao linnkManDao; @Test @Transactional @Rollback(value=false) public void save(){ Customer customer = new Customer(); customer.setCustName("百度"); LinkMan linkMan = new LinkMan(); linkMan.setLkmName("小明"); linkMan.setCustomer(customer); customerDao.save(customer); linnkManDao.save(linkMan); } }
这里操作了两张表要保证事务的一致性 @Transactional,其次事务操作成功之后要让更新的记录保存到数据库,不进行回滚 @Rollback(value=false)