spring data jpa
jpa:java persistence api java持久化api
orm思想
- 操作实体类相当于操作数据库表,不在重点关注sql语句
- 两个映射:1-实体类和表 2-属性和字段
- 实现orm思想的框架如:mybatis、hibernate
jpa规范
- jdbc规范:封装好具体操作数据库接口、方法,只需修改对应驱动
- 内部由接口和抽象类组成
- jpa规范不干活,依托实现方式(如:hibernate)来操作。
jpa使用操作
- 1-jar依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.hibernate.version>5.0.7.Final</project.hibernate.version>
</properties>
<dependencies>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- hibernate对jpa的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- log日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- Mysql and MariaDB -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
- 2-配置文件 需在class目录下的META-INF目录及名称为persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!--需配置persistence-unit节点
持久化单元:
name:持久化单元名称
transaction-type:事务管理方式 JTA=分布式事务管理 RESOURCE_LOCAL=本地事务管理
-->
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<!--jpa实现方式-->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<!--数据库信息:
用户名 javax.persistence.jdbc.user
密码 javax.persistence.jdbc.password
驱动 javax.persistence.jdbc.driver
地址 javax.persistence.jdbc.url
-->
<property name="javax.persistence.jdbc.user" value="root"></property>
<property name="javax.persistence.jdbc.password" value="root"></property>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"></property>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///mydb"></property>
<!--可选配置:配置jpa实现方式的配置信息
显示sql true=显示 false=不显示
自动创建数据库表 :
create=程序运行时创建数据库表,有表先删再创建
update=程序运行时创建数据库表 有表不会创建
none=不会创建数据库表
-->
<property name="hibernate.show_sql" value="true"></property>
<property name="hibernate.hbm2ddl.auto" value="update"></property>
</properties>
</persistence-unit>
</persistence>
- 3-实体类与表映射
package com.xiaoai.domain;
import javax.persistence.*;
/**
* 客户实体类 需要配置映射关系
* 1-实体类和表映射
* 2-属性和字段映射
*
* 主键生成策略
* IDENTITY=自增 底层必须支持自增 mysql
* SEQUENCE=序列 底层必须支持序列 oracle
* TABLE=jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
* AUTO=由程序自动帮助我们选择主键生成策略
*/
@Entity //1-1声明此类为实体类
@Table(name = "mjfs14_customer") //1-2配置实体类和表映射
public class Customer {
//2-1配置属性字段映射
@Id //声明主键
@GeneratedValue(strategy = GenerationType.IDENTITY) //配置主键生成策略 IDENTITY=自增 (要求:底层数据库必须支持自动增长)
@Column(name = "cust_id") //对应数据库字段名称
private Long custId; //客户主键
@Column(name = "cust_name")
private String custName;//客户名称
@Column(name = "cust_source")
private String custSource;//客户来源
@Column(name = "cust_level")
private String custLevel;//客户级别
@Column(name = "cust_industry")
private String custIndustry;//客户所属行业
@Column(name = "cust_phone")
private String custPhone;//客户联系方式
@Column(name = "cust_address")
private String custAddress;//客户地址
......对应属性get和set方法以及toString方法
- 4-具体CRUD操作
EntityManager相关方法 - presist(对象) 保存
- merge(对象) 更新 先查询得到对象,修改对象然后执行修改
- remove(对象) 删除 先查询得到对象,传入对象进行删除
- find(待封装对象class,id)/getRefrence(待封装对象class,id) 根据id查询 find立即加载,getRefrence延时加载(懒加载)
package com.xiaoai.test;
import com.sun.xml.internal.txw2.TXW;
import com.xiaoai.domain.Customer;
import com.xiaoai.utils.JpaUtils;
import org.junit.Test;
import javax.persistence.*;
/**
* jpa相应操作
*
* jpa操作步骤:
* 1-加载配置文件创建工厂(实体管理器工厂)对象
* EntityManagerFactory创建过程比较浪费资源
* 特点:线程安全对象:多个线程访问同一个EntityManagerFactory不会有线程安全问题
* ---解决EntityManagerFactory创建过程浪费资源(耗时)问题?
* 思路:创建公共EntityManagerFactory对象 解决:java中可以使用静态代码块形式创建EntityManagerFactory
* 2-通过实体管理类工厂获取实体管理器
* 方法:createEntityManager
* 内部维护很多内容:数据库信息、缓存信息、所有实体类管理器对象等
*
* createEntityManager对象:实体类管理器 方法:
* beginTransaction=创建事务对象
* presist=保存
* merge=更新
* remove=删除
* find/getRefrence=根据id查询
* 3-获取事务对象,开启事务
* EntityTransaction 事务对象
* begin=开启事务
* commit=提交事务
* rellback=回滚
* 4-完成相关增删改查操作
* 5-提交事务(回滚事务)
* 6-释放资源
*/
public class JpaTest {
/**
* -----测试jpa的增加 presist
*/
@Test
public void testSave(){
//1-加载配置文件创建工厂(实体管理器工厂) 根据持久化单元名称创建实体类工厂
EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
//2-获取实体类管理器
EntityManager em = factory.createEntityManager();
//3-获取事务管理 打开事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//4-执行操作 增加 presist
Customer customer = new Customer();
customer.setCustName("xiaoai");
customer.setCustIndustry("it");
em.persist(customer);
//5-提交事务
tx.commit();
//6-释放资源
em.close();
factory.close();
}
/**
* -----根据id查询 使用find方法
* 1-查询对象就是当前待查询对象本身
* 2-调用find方法时,就会发送sql语句查询数据库
*
* 立即加载:得到的即对象本身,调用方法就完成加载
*/
@Test
public void testFind(){
//获取管理器
EntityManager entityManager = JpaUtils.getEntityManager(); //通过JpaUtils封装生成同一EntityManagerFactory对象,减少资源耗费及耗时
//获取事务 打开事务
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//操作
Customer customer = entityManager.find(Customer.class, 1l);
System.out.println(customer);
//提交事务
tx.commit();
//关闭 由于使用同一个EntityManagerFactory对象,所以其暂时不能关闭
entityManager.close();
}
/**
* -----根据id查询 使用getReference方法
* 1-查询对象是一个动态代理对象
* 2-不会即时发送sql语句查询数据库,当调用查询结果对象的时候,才会发送查询的sql语句(即什么时候用什么时候发送)
* 延时加载(懒加载):得到的是一个动态代理对象 什么时候用什么时候加载
*
* 一般使用延时加载,
*/
@Test
public void testGetReference(){
//获取管理器
EntityManager entityManager = JpaUtils.getEntityManager();
//获取事务 打开事务
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//操作
Customer customer = entityManager.getReference(Customer.class, 1l);//getReference
System.out.println(customer);
//提交事务
tx.commit();
//关闭 由于使用同一个EntityManagerFactory对象,所以其暂时不能关闭
entityManager.close();
}
//-----删除 remove
@Test
public void testRemove(){
//获取管理器
EntityManager entityManager = JpaUtils.getEntityManager();
//获取事务 打开事务
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//操作 删除 先查后删
//--1-根据id查询客户
Customer customer = entityManager.find(Customer.class, 1l);//getReference
//--2-传入所查询结果删除
entityManager.remove(customer);
//提交事务
tx.commit();
//关闭 由于使用同一个EntityManagerFactory对象,所以其暂时不能关闭
entityManager.close();
}
//-----更新 merge
@Test
public void testUpdate(){
//获取管理器
EntityManager entityManager = JpaUtils.getEntityManager();
//获取事务 打开事务
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//操作 更新 先查询获取对象,修改对象后更新对象
//--1-根据id查询客户
Customer customer = entityManager.find(Customer.class, 2l);//getReference
//--2-修改对象信息
customer.setCustIndustry("net");
//--2-传入所查询结果删除
entityManager.merge(customer);
//提交事务
tx.commit();
//关闭 由于使用同一个EntityManagerFactory对象,所以其暂时不能关闭
entityManager.close();
}
}
EntityManagerFactory封装
package com.xiaoai.utils;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/**
* 解决实体管理器工厂的浪费资源和耗时问题
* 通过静态代码块的形式,当程序第一次访问此工具类是,创建一个公共的实体管理器工厂对象
*
* 1-第一次访问getEntityManager方法,经静态代码块创建factory对象,再调用方法创建一个EntityManager对象
* 2-第二次访问getEntityManager方法,直接通过一个已经创建好的factory对象创建EntityManager对象
*/
public class JpaUtils {
private static EntityManagerFactory factory;
static {
//加载配置文件,创建EntityManagerFactory
factory = Persistence.createEntityManagerFactory("myJpa");
}
/**
* 用来获取EntityManager对象
*/
public static EntityManager getEntityManager(){
return factory.createEntityManager();
}
}
jpql查询 java persistence query language jpa提供的一种查询语句 和sql差不多
sql:查询的是表和表中的字段
jpql:查询的是实体类和类中属性
步骤:
- 1-EntityManagerFactory 工厂
- 2-EntityManager 实体类管理器
- 3-EntityTransaction 打开事务
- 4-Query:jpql具体查询语句获取query对象,query对象执行查询 (jpa直接通过EntityManager对象进行操作)
- 5-提交事务
- 6-释放资源
package com.xiaoai.test;
import com.xiaoai.utils.JpaUtils;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import java.util.List;
/**
* jpql查询
*/
public class JpqlTest {
/**
* -----查询全部
* sql:select * from mjfs14_customer
* jqpl:from com.xiaoai.domain.Customer(可以使用全限定类名也可以简写: from Customer)
*/
@Test
public void testFindAll(){
//获取entityManager对象
EntityManager entityManager = JpaUtils.getEntityManager();
//开启事务
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//操作
//--1获取query对象
String jpql = "from com.xiaoai.domain.Customer";
Query query = entityManager.createQuery(jpql);
//--2具体操作,封装结果集
List list = query.getResultList();
for (Object obj:list ) {
System.out.println(obj);
}
//提交事务
tx.commit();
//释放资源
entityManager.close();
/*结果如下:
Hibernate: select customer0_.cust_id as cust_id1_0_, customer0_.cust_address as cust_add2_0_, customer0_.cust_industry as cust_ind3_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 mjfs14_customer customer0_
Customer{custId=1, custName='xiaoai2', custSource='null', custIndustry='it', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=2, custName='xiaoai', custSource='null', custIndustry='net', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=3, custName='xiaoai3', custSource='null', custIndustry='无业游民', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=4, custName='小艾', custSource='null', custIndustry='教育', custLevel='null', custAddress='null', custPhone='null'}
*/
}
/**
* -----倒叙查询
* sql: select * from mjfs14_customer order by cust_id DESC
* jpql: from Customer order by custId desc
*/
@Test
public void testOrders(){
//获取entityManager对象
EntityManager entityManager = JpaUtils.getEntityManager();
//开启事务
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//操作
//--1获取query对象
String jpql = "from Customer order by custId desc";
Query query = entityManager.createQuery(jpql);
//--2具体操作,封装结果集
List list = query.getResultList();
for (Object obj:list ) {
System.out.println(obj);
}
//提交事务
tx.commit();
//释放资源
entityManager.close();
/*结果如下:
Hibernate: select customer0_.cust_id as cust_id1_0_, customer0_.cust_address as cust_add2_0_, customer0_.cust_industry as cust_ind3_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 mjfs14_customer customer0_ order by customer0_.cust_id desc
Customer{custId=4, custName='小艾', custSource='null', custIndustry='教育', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=3, custName='xiaoai3', custSource='null', custIndustry='无业游民', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=2, custName='xiaoai', custSource='null', custIndustry='net', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=1, custName='xiaoai2', custSource='null', custIndustry='it', custLevel='null', custAddress='null', custPhone='null'}
*/
}
/**
* -----统计查询
* sql: select count(cust_id) from mjfs14_customer
* jpql: select count(custId) from Customer
*/
@Test
public void testCount(){
//获取entityManager对象
EntityManager entityManager = JpaUtils.getEntityManager();
//开启事务
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//操作
//--1获取query对象
String jpql = "select count(custId) from Customer";
Query query = entityManager.createQuery(jpql);
//--2具体操作,封装结果集
Object result = query.getSingleResult();//得到唯一的结果集
System.out.println(result);
//提交事务
tx.commit();
//释放资源
entityManager.close();
/*结果如下:
Hibernate: select count(customer0_.cust_id) as col_0_0_ from mjfs14_customer customer0_
4
*/
}
/**
* -----分页查询
* sql: select * from mjfs14_customer limit ?,?
* jpql: from Customer
*/
@Test
public void testPaged(){
//获取entityManager对象
EntityManager entityManager = JpaUtils.getEntityManager();
//开启事务
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//操作
//--1获取query对象
String jpql = "from Customer";
Query query = entityManager.createQuery(jpql);
//--2具体操作,封装结果集
//----设置参数(分页参数)
query.setFirstResult(0);//从下标0开始查
query.setMaxResults(2);//每次查询两条
List list = query.getResultList();
for (Object obj:list ) {
System.out.println(obj);
}
//提交事务
tx.commit();
//释放资源
entityManager.close();
/*结果如下:
Hibernate: select customer0_.cust_id as cust_id1_0_, customer0_.cust_address as cust_add2_0_, customer0_.cust_industry as cust_ind3_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 mjfs14_customer customer0_ limit ?
Customer{custId=1, custName='xiaoai2', custSource='null', custIndustry='it', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=2, custName='xiaoai', custSource='null', custIndustry='net', custLevel='null', custAddress='null', custPhone='null'}
*/
}
/**
* -----条件查询
* sql: select * from mjfs14_customer where cust_name like ?
* jpql: from Customer where custName like ?
*/
@Test
public void testCondition(){
//获取entityManager对象
EntityManager entityManager = JpaUtils.getEntityManager();
//开启事务
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//操作
//--1获取query对象
String jpql = "from Customer where custName like ?";
Query query = entityManager.createQuery(jpql);
//--2具体操作,封装结果集
//----设置参数(占位符参数)
query.setParameter(1,"xiaoai%");//参数:1=第几个占位符 2=代替值
List list = query.getResultList();
for (Object obj:list ) {
System.out.println(obj);
}
//提交事务
tx.commit();
//释放资源
entityManager.close();
/*结果如下:
Hibernate: select customer0_.cust_id as cust_id1_0_, customer0_.cust_address as cust_add2_0_, customer0_.cust_industry as cust_ind3_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 mjfs14_customer customer0_ where customer0_.cust_name like ?
Customer{custId=1, custName='xiaoai2', custSource='null', custIndustry='it', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=2, custName='xiaoai', custSource='null', custIndustry='net', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=3, custName='xiaoai3', custSource='null', custIndustry='无业游民', custLevel='null', custAddress='null', custPhone='null'}
*/
}
}
spring data jpa
相互关系:我们写的java代码——》spring data jpa——》jpa规范——》hibernate(封装jdbc)——》数据库
基础使用
- 导入jar
<properties>
<spring.version>4.2.4.RELEASE</spring.version>
<hibernate.version>5.0.7.Final</hibernate.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<c3p0.version>0.9.1.2</c3p0.version>
<mysql.version>5.1.6</mysql.version>
</properties>
<dependencies>
<!-- junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
<!-- spring beg -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring end -->
<!-- hibernate beg -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.1.Final</version>
</dependency>
<!-- hibernate end -->
<!-- c3p0 beg -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<!-- c3p0 end -->
<!-- log end -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.9.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- el beg 使用spring data jpa 必须引入 -->
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
<!-- el end -->
</dependencies>
- 配置spring配置文件(配置spring data 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: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对象-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"></property><!--数据源-->
<property name="packagesToScan" value="com.xiaoai.domain"></property><!--扫描实体类所在包-->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"></bean><!--配置jpa的实现厂家-->
</property>
<!--JPA的供应商适配器-->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--配置是否自动创建数据库表-->
<property name="generateDdl" value="false" /><!--是否自动创建表-->
<property name="database" value="MYSQL" /> <!--数据库类型-->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" /><!--数据库方言:数据库支持的特有语法-->
<property name="showSql" value="true" /><!--是否显示sql语句:即是否打印到控制台-->
</bean>
</property>
<!--jpa方言,高级特性-->
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean>
</property>
</bean>
<!--2=创建数据库连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value="root"></property>
<property name="jdbcUrl" value="jdbc:mysql:///mydb?useUnicode=true&characterEncoding=UTF-8"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
</bean>
<!--3=整合spring dataJpa-->
<jpa:repositories base-package="com.xiaoai.dao"
transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory">
</jpa:repositories>
<!--4=配置事务管理器-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<!--5=spring声明式事务-->
<!--6=配置包扫描-->
<context:component-scan base-package="com.xiaoai"></context:component-scan>
</beans>
- 编写实体类及映射 同jpa一致
- 编写dao层
- 只要编写dao接口,不需编写实现类
- 接口需继承两接口:JpaRepository, JpaSpecificationExecutor 接口需提供响应泛型
- JpaRepository<操作的实体类类型,实体类类型主键属性的类型> 封装了基本curd操作
- JpaSpecificationExecutor<操作的实体类类型> 封装了复杂查询(分页等)
package com.xiaoai.dao;
import com.xiaoai.domain.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface CustomerDao extends JpaRepository<Customer,Long>,JpaSpecificationExecutor<Customer> {
}
- 注入接口对象,直接使用
接口相关方法
- findOne(id) 根据id查询
- save(对象) 对象标识主键 不存在=新增 存在=修改(修改需要先查询然后修改,不要直接创建相同id的对象然后直接执行修改)
- delete(id) 根据id删除
- findAll() 查询所有
- count() 统计总数量
- exists(id) 查询id为xx的数据是否存在
- 思路1:查询id为xx数据,判断值是否为空
- 思路2:判断id为xx的数据数量,数量为0即不存在 JPA即是该种思路
- getOne(id) 根据id查询 测试方法上需要 @Transactional支持
- 与findOne区别:findOne底层调用entityManager的find()方法即立即加载,GetOne底层调用entityManager的getRefrence()方法即延时加载。为了延时加载正常执行需要加上事务支持
package com.xiaoai.test;
import com.xiaoai.dao.CustomerDao;
import com.xiaoai.domain.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class CustomerDaoTest {
@Autowired
CustomerDao customerDao;
/**
* -----根据id查询
*/
@Test
public void testFindOne(){
Customer one = customerDao.findOne(3l);
System.out.println(one);
/*结果
Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_address as cust_add2_0_0_, customer0_.cust_industry as cust_ind3_0_0_, customer0_.cust_level as cust_lev4_0_0_, customer0_.cust_name as cust_nam5_0_0_, customer0_.cust_phone as cust_pho6_0_0_, customer0_.cust_source as cust_sou7_0_0_ from mjfs14_customer customer0_ where customer0_.cust_id=?
Customer{custId=3, custName='xiaoai3', custSource='null', custIndustry='无业游民', custLevel='null', custAddress='null', custPhone='null'}
*/
}
/**
* -----save:保存或者更新,根据传入的对象主键id是否存在判断新增或更新
*/
@Test
public void testSave(){
//新增
Customer customer = new Customer();
customer.setCustName("程序员");
customer.setCustLevel("3");
customer.setCustIndustry("it");
customerDao.save(customer);
}
/**
* -----save:保存或者更新,根据传入的对象主键id是否存在判断新增或更新
*/
@Test
public void testUpdate(){
// Customer customer = new Customer();
// customer.setCustId(5l); //id为5的数据已经存在,
// customer.setCustName("程序员");
// customerDao.save(customer);
//如果像上面那样修改,则id为5的数据名称改为了程序员,但其他属性将变为空。
// 因为修改是查询然后修改,这里直接定义了新对象设置了名称其他属性都为空,即用其替换了原来的5号数据
//应该先查询获得对象,然后修改对象再执行修改。
Customer customer = customerDao.findOne(5l);
customer.setCustName("程序员");//修改编号5数据的名称为程序员
customerDao.save(customer);
}
/**
* -----删除
*/
@Test
public void testDelete(){
customerDao.delete(6l);
}
/**
* -----查询所有
*/
@Test
public void testFindAll(){
List<Customer> all = customerDao.findAll();
for (Customer c : all) {
System.out.println(c);
}
/* 测试结果:
Hibernate: select customer0_.cust_id as cust_id1_0_, customer0_.cust_address as cust_add2_0_, customer0_.cust_industry as cust_ind3_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 mjfs14_customer customer0_
Customer{custId=1, custName='xiaoai2', custSource='null', custIndustry='it', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=2, custName='xiaoai', custSource='null', custIndustry='net', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=3, custName='xiaoai3', custSource='null', custIndustry='无业游民', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=4, custName='小艾', custSource='null', custIndustry='教育', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=5, custName='程序员', custSource='null', custIndustry='it', custLevel='3', custAddress='null', custPhone='null'}
*/
}
/**
*-----统计查询
*/
@Test
public void testCount(){
long count = customerDao.count();
System.out.println(count);
/*测试结果:
Hibernate: select count(*) as col_0_0_ from mjfs14_customer customer0_
5
*/
}
/**
* -----判断数据是否存在
*/
@Test
public void testExists(){
boolean exists = customerDao.exists(4l);
System.out.println("是否存在:"+exists);
/*测试结果:
Hibernate: select count(*) as col_0_0_ from mjfs14_customer customer0_ where customer0_.cust_id=? and 1=1
是否存在:true
*/
}
/**
* -----根据id查询
*/
@Test
@Transactional //保证getOne正常运行
public void testGetOne(){
Customer one = customerDao.getOne(4l);
System.out.println(one);
/*测试结果:
Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_address as cust_add2_0_0_, customer0_.cust_industry as cust_ind3_0_0_, customer0_.cust_level as cust_lev4_0_0_, customer0_.cust_name as cust_nam5_0_0_, customer0_.cust_phone as cust_pho6_0_0_, customer0_.cust_source as cust_sou7_0_0_ from mjfs14_customer customer0_ where customer0_.cust_id=?
Customer{custId=4, custName='小艾', custSource='null', custIndustry='教育', custLevel='null', custAddress='null', custPhone='null'}
*/
}
}
定义jpql/sql语句查询
- 特有查询:需要在dao接口上配置方法
- 新添加的方法上使用注解形式配置jpql语句
- 注解:@query
- 注解属性value=jpql语句或sql语句
- 注解属性nativeQuery=false(使用jqpl查询)或true(使用本地查询:即sql查询)
- jpql更深层封装:只需要按照springDataJpa提供的方法名称规则定义方法,不需要去配置jpql语句,也可以完成查询
- findBy+属性名称() = 查询 如:findByCustName 根据方法名称(findBy)解析要查询 from 实体类 where 属性名称(如custName) = 参数
- findBy+属性名称+查询方式(like、isnull) 如:findByCustNameLike
- findBy+属性名+查询方式+多条件连接符(and、or)+属性名+查询方式(精准匹配可以省略查询方式) 如:findByCustNameLikeAndCustIndustry
接口定义的方法
package com.xiaoai.dao;
import com.xiaoai.domain.Customer;
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 java.util.List;
/**
* 定义符合springdataJpa的dao层接口规范
* JpaRepository<操作的实体类类型,实体类类型主键属性的类型>
* 封装了基本curd操作
* JpaSpecificationExecutor<操作的实体类类型>
* 封装了复杂查询(分页)
*/
public interface CustomerDao extends JpaRepository<Customer,Long>,JpaSpecificationExecutor<Customer> {
//===============================================================自定义jpql执行方法
//使用注解@Query
//-----根据客户名称查询客户
@Query(value = "from Customer where custName=?")
public Customer jpqlFindByName(String custName);
//-----根据客户名称id查询客户
// 需要注意多占位符的匹配关系问题
// 默认情况下,占位符位置需要和方法参数中位置保持一致
// 但也可以指定占位符参数位置:通过【?索引】的方式指定此占位符的取值来源 如?1 表示该占位符取方法第一个参数
@Query(value = "from Customer where custName=?1 and custId=?2")
public Customer jpqlFindByNameAndId(String name,Long id);
//-----根据id更新客户名称
@Query(value = "update Customer set custName=?2 where custId=?1")
@Modifying //声明此方法是用来进行更新操作
public void jpqlUpdateNameById(Long custId,String custName);
//===============================================================自定义sql语句执行方法
// @Query 属性:value=jpql语句或sql语句 nativeQuery=false(使用jqpl查询)或true(使用本地查询:即使用sql查询)
//-----查询所有
@Query(value = "select * from mjfs14_customer",nativeQuery = true)
public List<Object[]> findSql();
//-----根据名称模糊查询
@Query(value = "select * from mjfs14_customer where cust_name like ?1",nativeQuery = true)
public List<Object[]> findSqlByName(String name);
//===============================================================jpql的深层封装:根据springDataJpa提供的命名方法
// findByXxx = 查询 Xxx即对象属性即查询条件 如:findByCustName 根据方法名称(findBy)解析要查询 from 实体类 where xxx(属性名称如custName) = 参数
//-----根据名称查询
public Customer findByCustName(String custName);
//-----根据名称模糊查询
public List<Customer> findByCustNameLike(String custName);
//-----查询客户名模糊匹配和客户所属行业精准匹配
public List<Customer> findByCustNameLikeAndCustIndustry(String custName,String custIndustry);
}
测试
package com.xiaoai.test;
import com.xiaoai.dao.CustomerDao;
import com.xiaoai.domain.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import javax.management.Query;
import java.util.Arrays;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class JpqlTest {
@Autowired
CustomerDao customerDao;
//-----自定义jpql:根据名称查询
@Test
public void testJpqlFindByName(){
Customer customer = customerDao.jpqlFindByName("xiaoai");
System.out.println(customer);
/* 执行结果:
Hibernate: select customer0_.cust_id as cust_id1_0_, customer0_.cust_address as cust_add2_0_, customer0_.cust_industry as cust_ind3_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 mjfs14_customer customer0_ where customer0_.cust_name=?
Customer{custId=2, custName='xiaoai', custSource='null', custIndustry='net', custLevel='null', custAddress='null', custPhone='null'}
*/
}
//-----自定义jpql:根据名称和id查询
@Test
public void testJpqlFindByNameAndId(){
Customer customer = customerDao.jpqlFindByNameAndId("xiaoai",2l);
System.out.println(customer);
/* 执行结果:
Hibernate: select customer0_.cust_id as cust_id1_0_, customer0_.cust_address as cust_add2_0_, customer0_.cust_industry as cust_ind3_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 mjfs14_customer customer0_ where customer0_.cust_name=? and customer0_.cust_id=?
Customer{custId=2, custName='xiaoai', custSource='null', custIndustry='net', custLevel='null', custAddress='null', custPhone='null'}
*/
}
//-----自定义jpql:根据id更新客户名称
@Test
@Transactional //spring data jpa 使用jpql完成更新、删除操作需要手动添加事务支持 执行结束后其会自动回滚事务
@Rollback(value = false) //设置不自动回滚
public void testJpqlUpdateNameById(){
customerDao.jpqlUpdateNameById(3l,"程序员小艾");
Customer customer = customerDao.findOne(3l);
System.out.println(customer);
/* 执行结果:
Hibernate: update mjfs14_customer set cust_name=? where cust_id=?
Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_address as cust_add2_0_0_, customer0_.cust_industry as cust_ind3_0_0_, customer0_.cust_level as cust_lev4_0_0_, customer0_.cust_name as cust_nam5_0_0_, customer0_.cust_phone as cust_pho6_0_0_, customer0_.cust_source as cust_sou7_0_0_ from mjfs14_customer customer0_ where customer0_.cust_id=?
Customer{custId=3, custName='程序员小艾', custSource='null', custIndustry='无业游民', custLevel='null', custAddress='null', custPhone='null'}
*/
}
//-----自定义sql:查询所有
@Test
public void testFindSql(){
List<Object[]> list = customerDao.findSql();
for (Object[] obj : list) {
System.out.println(Arrays.toString(obj));
}
/* 执行结果:
Hibernate: select * from mjfs14_customer
[1, xiaoai2, null, it, null, null, null]
[2, xiaoai, null, net, null, null, null]
[3, 程序员小艾, null, 无业游民, null, null, null]
[4, 小艾, null, 教育, null, null, null]
[5, 程序员, null, it, 3, null, null]
*/
}
//-----自定义sql:根据名称模糊查询
@Test
public void testFindSqlByName(){
List<Object[]> list = customerDao.findSqlByName("xiaoai%");
for (Object[] obj : list) {
System.out.println(Arrays.toString(obj));
}
/* 执行结果:
Hibernate: select * from mjfs14_customer where cust_name like ?
[1, xiaoai2, null, it, null, null, null]
[2, xiaoai, null, net, null, null, null]
*/
}
//-----jpql进一步封装:根据名称查询
@Test
public void testFindByName(){
Customer customer = customerDao.findByCustName("小艾");
System.out.println(customer);
/* 执行结果:
Hibernate: select customer0_.cust_id as cust_id1_0_, customer0_.cust_address as cust_add2_0_, customer0_.cust_industry as cust_ind3_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 mjfs14_customer customer0_ where customer0_.cust_name=?
Customer{custId=4, custName='小艾', custSource='null', custIndustry='教育', custLevel='null', custAddress='null', custPhone='null'}
*/
}
//-----jpql进一步封装:根据名称模糊查询
@Test
public void testFindByNameLike(){
List<Customer> customers = customerDao.findByCustNameLike("xiaoai%");
for (Customer c : customers) {
System.out.println(c);
}
/* 执行结果:
Hibernate: select customer0_.cust_id as cust_id1_0_, customer0_.cust_address as cust_add2_0_, customer0_.cust_industry as cust_ind3_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 mjfs14_customer customer0_ where customer0_.cust_name like ?
Customer{custId=1, custName='xiaoai2', custSource='null', custIndustry='it', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=2, custName='xiaoai', custSource='null', custIndustry='net', custLevel='null', custAddress='null', custPhone='null'}
*/
}
//-----jpql进一步封装:客户名模糊匹配和客户所属行业精准匹配
@Test
public void testFindByNAndI(){
List<Customer> customers = customerDao.findByCustNameLikeAndCustIndustry("xiaoai%","it");
for (Customer c : customers) {
System.out.println(c);
}
/* 执行结果:
Hibernate: select customer0_.cust_id as cust_id1_0_, customer0_.cust_address as cust_add2_0_, customer0_.cust_industry as cust_ind3_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 mjfs14_customer customer0_ where (customer0_.cust_name like ?) and customer0_.cust_industry=?
Customer{custId=1, custName='xiaoai2', custSource='null', custIndustry='it', custLevel='null', custAddress='null', custPhone='null'}
*/
}
}
specifications动态查询
Specification即查询条件
即接口继承的JpaSpecificationExecutor类所提供的方法列表:
- T findOne(Specification
var1); 单个查询 - List
findAll(Specification var1); 查询列表 - Page
findAll(Specification var1, Pageable var2); 分页查询 pageable=分页参数 - List
findAll(Specification var1, Sort var2); 查询列表 sort=排序参数 - long count(Specification
var1); 统计查询
**使用**
- 创建Specification对象,通过匿名内部类构建查询条件,再把对象传入JpaSpecificationExecutor提供的方法中完成查询
- 匿名内部类方法参数:Root
获取查询的对象属性 get()=获取属性 - 匿名内部类方法参数:CriteriaBuilder 用于构建条件 .equal()=精准查询 .like()=模糊查询 .and()=连接属性
- 匿名内部类方法参数:Root
- equal:直接得到path对象,然后进行比较
- gt,lt,ge,le,like:得到path对象,根据path指定比较的参数类型,再去进行比较 指定参数类型格式:path对象.as(类型字节码对象) 如: Predicate like = criteriaBuilder.like(custName.as(String.class), "xiaoai%");
- 排序:创建排序对象做参数传入对应方法 Sort sort = new Sort(Sort.Direction.DESC,"custId");//倒序
- 分页:创建分页对象做参数传入对应方法
@Test
public void testSpec5(){
//添加分页
Pageable pageable = new PageRequest(0,3); //创建分页对象 参数:1=当前查询的页数(从0开始) 2=每页查询数量
Page<Customer> pageCustomer = customerDao.findAll(null, pageable);//Specification传入空,表示查询全部
System.out.println("总条数:"+pageCustomer.getTotalElements());
System.out.println("总页数:"+pageCustomer.getTotalPages());
List<Customer> content = pageCustomer.getContent();//获取当前页数据列表
for (Customer c : content) {
System.out.println(c);
}
/*执行结果:
Hibernate: select count(customer0_.cust_id) as col_0_0_ from mjfs14_customer customer0_
Hibernate: select customer0_.cust_id as cust_id1_0_, customer0_.cust_address as cust_add2_0_, customer0_.cust_industry as cust_ind3_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 mjfs14_customer customer0_ limit ?
总条数:5
总页数:2
Customer{custId=1, custName='xiaoai2', custSource='null', custIndustry='it', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=2, custName='xiaoai', custSource='null', custIndustry='net', custLevel='null', custAddress='null', custPhone='null'}
Customer{custId=3, custName='程序员小艾', custSource='null', custIndustry='无业游民', custLevel='null', custAddress='null', custPhone='null'}
*/
}
多表操作
一对多
- 主要是配置实体类映射时和其他实体类的关系
- 一对一
- 一对多、多对一 注:在一的一方维护外键即可,不需要一和多都维护外键
- 多对多
- 可以添加实体类属性并配置其关系映射可完成多表操作
- 配置相互关系可用实体类间的包含关系,即在实体类中添加另一个实体类作为属性。并需要配置其关系:1-声明表关系 2-配置外键
多的一方:
@OneToMany(targetEntity = LinkMan.class) //配置一对多关系 需对方字节码对象
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id") //name=从表的外键字段,referencedColumnName=主表的主键字段
private Set<LinkMan> linkmans = new HashSet<>();
一的一方:
//配置联系人到客户多对一关系 1-声明表关系 2-配置外键
@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id") //name=从表的外键字段,referencedColumnName=本表的主键字段
private Customer customer;
多的一方放弃维护外键:mappedBy="对方配置关系的属性名"
@OneToMany(mappedBy="customer")
private Set<LinkMan> linkmans = new HashSet<>(); //放弃外键维护权,表关系交给一的一方维护
- 一对多删除:删除【一一方】需要考虑,因为有可能【多一方】引入了【一一方】 在【一一方】删除时则需要使用级联删除
- 级联:操作一个对象的同时操作他的关联对象
- 级联删除:删除【一一方】的同时删除关联其的【多一方】的所有数据。想级联删除需要在操作的主体即【一一方】的实体类上添加级联属性(配置级联:cascade)
配置级联:
@OneToMany(mappedBy="customer",cascade = CascadeType.ALL) //CascadeType.ALL=所有操作(insert、delete等)都可级联
private Set<LinkMan> linkmans = new HashSet<>();
多对多
- 创建实体类配置映射,通过包含关系加入另一实体类做属性,配置实体类间多对多关系
//建立关联 1-声明表关系 2-配置中间表(包含两个外键)
@ManyToMany(targetEntity = Role.class)
@JoinTable(name = "mjfs14_user_role", //中间表名称 @JoinColumn和@inverseJoinColumns参数:name=对应外键字段 referencedColumnName=对应主键字段
joinColumns = {@JoinColumn(name = "center_user_id",referencedColumnName = "user_id")}, // 当前对象在中间表中对应外键配置
inverseJoinColumns = {@JoinColumn(name = "center_role_id",referencedColumnName = "role_id")} //对方对象在中间表中对应外键配置
)
private Set<Role> roles = new HashSet<>();
- 放弃外键维护权:两方都是多,随便一方都可以放弃外键维护权 但是一般被选择一方放弃 放弃维护权和【一对多】一致
- 级联操作:删除对象以及对象关联的对象以及中间表中关联的数据 操作和一对多一致
对象导航查询
- 查询一个对象时,通过此对象查询其关联对象 操作即:直接通过对象.getxxx()方法即之前创建关联的对象属性时的get方法获取即可 内部自动查询好了
- 一方查询多方:默认使用延迟加载(因为在一方查询多方,有可能多方有太多数据,立即加载后不用则极大浪费内存) 多方查询一方:默认使用立即加载(多方查询一方时由于只多一条数据,影响不大,所以立即查询了)
- 延迟加载: 默认即使用延迟加载的形式查询的 可以修改延迟加载为立即加载 即在实体类映射中通过@OneToMany属性fetch配置 EAGER=立即加载 LAZY=延迟加载
配置立即加载:
//使用注解形式配置多表关系 1-声明关系 2-配置外键
@OneToMany(mappedBy="customer",cascade = CascadeType.ALL,fetch = FetchType.EAGER ) //CascadeType.ALL=所有操作(insert、delete等)都可级联 FetchType.EAGER
private Set<LinkMan> linkmans = new HashSet<>();