P1.JPA
C1.HelloWorld
S1.步骤
step1.配置jpa的核心配置文件
-
persistent.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> <!--可选配置:配置jpa实现方的配置信息--> <properties> <!-- 数据库信息 用户名,javax.persistence.jdbc.user 密码, javax.persistence.jdbc.password 驱动, javax.persistence.jdbc.driver 数据库地址 javax.persistence.jdbc.url --> <property name="javax.persistence.jdbc.user" value="root"/> <property name="javax.persistence.jdbc.password" value="111111"/> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> <property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/> <!--配置jpa实现方(hibernate)的配置信息 显示sql : false|true 自动创建数据库表 : hibernate.hbm2ddl.auto create : 程序运行时创建数据库表(如果有表,先删除表再创建) update :程序运行时创建表(如果有表,不会创建表) none :不会创建表 --> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.hbm2ddl.auto" value="update" /> </properties> </persistence-unit> </persistence>
-
step2.编写客户实体类并配置映射关系
-
Customer.java
-
/** * 客户的实体类 */ @Entity @Table(name = "tbl_customer") public class Customer { /** * 客户id * @GeneratedValue => 主键的生成策略 * strategy => 主键生成策略 * GenerationType.IDENTITY => 自增 * 底层数据库必须支持自动增长 * MySql * GenerationType.SEQUENCE => 序列 * 底层数据库必须支持序列 * Oracle * GenerationType.TABLE => 通过一张数据库表的形式帮助我们完成主键自增 * JPA提供的一种机制 * GenerationType.AUTO => 由程序自动帮我们选择主键生成策略 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "customer_id") private Integer id; /** * 客户姓 */ @Column(name = "last_name", length = 50, nullable = false) private String lastName; /** * 电子邮件 */ @Column(length = 50) private String email; // 略 }
-
S2.测试步骤
step1.加载配置文件,创建工厂对象(实体管理类工厂)
-
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("持久化单元名称");
-
静态方法
-
根据持久化单元名称创建实体管理器工厂
-
Persistence
对象- 主要作用:
- 创建实体管理器工厂
- 主要作用:
-
EntityManagerFactory
对象 => 实体管理器工厂-
主要作用:获取
EntityManager
对象 -
内部维护了很多的内容:
- 维护了数据库信息
- 维护了所有的实体管理器对象
- 在创建
EntityManagerFactory
的过程中会根据配置创建数据库表
-
EntityManagerFactory
创建过程比较浪费资源-
如何解决?
-
创建一个公共的
EntityManagerFactory
对象 -
静态代码块
-
/** * 解决EntityManagerFactory创建耗费资源的问题 * 通过静态代码块的形式 * 当程序第一次访问此工具类是,创建一个公共的EntityManagerFactory对象 * * 第一访问getEntityManagerFactory(): * 经过静态代码块,创建一个factory对象,再调用方法创建一个EntityManager对象 * 第二访问getEntityManagerFactory(): * 直接通过一个已经创建好的factory对象,创建EntityManager对象 */ public class JpaUtil { private static EntityManagerFactory entityManagerFactory; static { entityManagerFactory = Persistence.createEntityManagerFactory("myJpa"); } /** * 获取EntityManager对象 */ public static EntityManager getEntityManagerFactory() { return entityManagerFactory.createEntityManager(); } }
-
-
-
线程安全的对象
- 多个线程访问同一个
EntityManagerFactory
不会有安全问题
- 多个线程访问同一个
-
-
step2.通过实体管理工程获取实体管理器
-
EntityManager entityManager = entityManagerFactory.createEntityManager();
-
EntityManager
对象- 实体类管理器
- 真正和数据库打交道的对象
- JPA操作的核心
- 用
EntityManager
里面的方法来完成增删改查,开启、获取事务对象 beginTransaction
:创建事务对象persist
:保存merge
:更新remove
:删除find/getReference
:根据id查询
- 用
step3.获取事务对象,开启事务
-
增删改之前要记得开启事务
-
// 获取事务对象 EntityTransaction tx = entityManager.getTransaction(); // 开启事务 tx.begin();
-
EntityTransaction
对象:- 事务
begin
:开启事务commit
:提交事务rollback
:回滚
step4.增删改查
step5.提交事务、回滚事务
step6.释放资源
-
public class TestStep { /** * 测试jpa的保存 * 保存一个用户到数据库中 */ @Test public void testSave() { System.out.println("testSave..."); // step1.加载配置文件,创建工厂对象(实体管理类工厂) EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("myJpa"); // step2.通过实体管理工程获取实体管理器 EntityManager entityManager = entityManagerFactory.createEntityManager(); // step3.获取事务对象,开启事务 EntityTransaction tx = entityManager.getTransaction(); // 获取事务对象 tx.begin(); // 开启事务 // step4.增删改查 Customer customer = new Customer(null, "zhangsan", "123@qq.com"); entityManager.persist(customer); // step5.提交事务、回滚事务 tx.commit(); // step6.释放资源 entityManager.close(); entityManagerFactory.close(); } }
S3.JpaUtil
-
起因:
EntityManagerFactory
创建过程比较浪费资源,所以要创建一个公共的EntityManagerFactory
对象
-
/** * 解决EntityManagerFactory创建耗费资源的问题 * 通过静态代码块的形式 * 当程序第一次访问此工具类是,创建一个公共的EntityManagerFactory对象 * * 第一访问getEntityManagerFactory(): * 经过静态代码块,创建一个factory对象,再调用方法创建一个EntityManager对象 * 第二访问getEntityManagerFactory(): * 直接通过一个已经创建好的factory对象,创建EntityManager对象 */ public class JpaUtil { private static EntityManagerFactory entityManagerFactory; static { entityManagerFactory = Persistence.createEntityManagerFactory("myJpa"); } /** * 获取EntityManager对象 */ public static EntityManager getEntityManagerFactory() { return entityManagerFactory.createEntityManager(); } }
C2.CRUD
persist
------------> 保存merge
--------------> 更新remove
-------------> 删除find/getReference
-> 查找
S1.插入
Customer customer = new Customer(null, "lisi", "123456@qq.com");
entityManager.persist(customer);
S2.查找
一、find
Customer customer = entityManager.find(Customer.class, 1);
- 立即加载
- 使用find方法查询:
- 获取的对象就是当前客户对象本身
- 在调用find方法时,就会发送sql语句查询数据库
二、getReference
Customer customer = entityManager.getReference(Customer.class, 1);
- 延迟加载,懒加载
- 一般使用这个方法
- 调用getReference方法查询:
- 获取的对象是一个动态代理对象
- 调用getReference方法不会立即发送sql语句查询数据库;
而是在调用查询结果对象的时候才会发送查询的sql语句- 什么时候用,什么时候发送sql语句查询数据库
S3.删除
Customer customer = entityManager.find(Customer.class, 1);
entityManager.remove(customer);
S4.更新
Customer customer = entityManager.find(Customer.class, 2);
customer.setLastName("王五");
entityManager.merge(customer);
S5.Demo
public class TestJpaUtil {
/**
* 测试jpa的保存
* 保存一个用户到数据库中
*/
@Test
public void testSave() {
System.out.println("testSave...");
EntityManager entityManager = JpaUtil.getEntityManager();
EntityTransaction tx = entityManager.getTransaction(); // 获取事务对象
tx.begin(); // 开启事务
Customer customer = new Customer(null, "lisi", "123456@qq.com");
entityManager.persist(customer);
tx.commit();
entityManager.close();
}
/**
* 根据id查询客户
*/
@Test
public void testFind() {
System.out.println("testFind...");
EntityManager entityManager = JpaUtil.getEntityManager();
Customer customer = entityManager.find(Customer.class, 1);
System.out.println("查询结果:" + customer);
entityManager.close();
}
/**
* 根据id查询客户
*/
@Test
public void testGetReference() {
System.out.println("testGetReference...");
EntityManager entityManager = JpaUtil.getEntityManager();
Customer customer = entityManager.getReference(Customer.class, 1);
System.out.println("查询结果:" + customer);
entityManager.close();
}
/**
* 测试jpa的删除
*/
@Test
public void testRemove() {
System.out.println("testRemove...");
EntityManager entityManager = JpaUtil.getEntityManager();
EntityTransaction tx = entityManager.getTransaction(); // 获取事务对象
tx.begin(); // 开启事务
Customer customer = entityManager.find(Customer.class, 1);
entityManager.remove(customer);
tx.commit();
entityManager.close();
}
/**
* 测试jpa的更新
*/
@Test
public void testUpdate() {
System.out.println("testUpdate...");
EntityManager entityManager = JpaUtil.getEntityManager();
EntityTransaction tx = entityManager.getTransaction(); // 获取事务对象
tx.begin(); // 开启事务
Customer customer = entityManager.find(Customer.class, 2);
customer.setLastName("王五");
entityManager.merge(customer);
tx.commit();
entityManager.close();
}
}
C3.JPQL
S1.简介
- JPA提供的一种查询语句
- Java Persistence Query Language
- 面向对象的查询方式
- SQL:查询的是表和表中的属性
JPQL:查询的是实体类和类中的属性 - 语法和SQL相似
- 会对SQL语句进行优化
S2.查询
- 查询的步骤:
- 编写jpql语句
- 根据jpql语句创建查询对象
- 查询,并得到返回结果
一、查询全部
- JPQL语句:
from xqy.jpa.entity.Customer
String jpql = "from xqy.jpa.entity.Customer";
Query query = entityManager.createQuery(jpql);
List list = query.getResultList();
二、分页查询
- SQL语句:
select * from customer limit ?,?
- JPQL语句:
from xqy.jpa.entity.Customer
String jpql = "from xqy.jpa.entity.Customer";
Query query = entityManager.createQuery(jpql);
query.setFirstResult(0);
query.setMaxResults(5);
List list = query.getResultList();
三、统计查询
- SQL语句:
select count(id) from customer
- JPQL语句:
select count(id) from xqy.jpa.entity.Customer
String jpql = "select count(id) from xqy.jpa.entity.Customer";
Query query = entityManager.createQuery(jpql);
Object singleResult = query.getSingleResult();
四、条件查询
- JPQL语句:
from xqy.jpa.entity.Customer where lastName like ?
String jpql = "from xqy.jpa.entity.Customer where lastName like ?";
Query query = entityManager.createQuery(jpql);
query.setParameter(1, "l%");
List list = query.getResultList();
五、排序
- SQL语句:
select * from customer order by id desc
- JPQL语句:
from xqy.jpa.entity.Customer order by id desc
String jpql = "from xqy.jpa.entity.Customer order by id desc";
Query query = entityManager.createQuery(jpql);
List list = query.getResultList();
P2.SpringDataJPA
C1.简述
-
- Spring Data JPA是Spring提供的一套对JPA操作更加高级的封装;
是在JPA规范下的专门来进行数据持久化的解决方案
C2.HelloWorld
S1.步骤
step1.配置Spring的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!-- Spring 和 Spring Data JPA的配置 -->
<!-- 1.创建entityManagerFactory对象交给Spring容器管理 -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- 配置数据源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 配置扫描的包(实体类所在的包) -->
<property name="packagesToScan" value="xqy.bean"/>
<!-- JPA的实现厂家 -->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"></bean>
</property>
<!-- JPA供应商的适配器 -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--配置是否自动创建数据库表 -->
<property name="generateDdl" value="false" />
<!--指定数据库类型 -->
<property name="database" value="MYSQL" />
<!--数据库方言:支持的特有语法 -->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
<!--是否显示sql -->
<property name="showSql" value="true" />
</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="123456"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://192.168.252.128:3306/jpa"></property>
</bean>
<!-- 3.整合Spring Data JPA -->
<jpa:repositories base-package="xqy.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.声明式事务 -->
<!-- 6.配置包扫描 -->
<context:component-scan base-package="xqy"></context:component-scan>
</beans>
step2.编写实体类
@Entity
@Table(name = "tbl_customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "customer_id")
private Integer id;
@Column(name = "last_name", length = 50, nullable = false)
private String lastName;
@Column(length = 50)
private String email;
// 略
}
step3.编写dao层接口
- 只需要编写dao层接口,不需要编写dao层接口的实现类
- dao层接口规范:
- 继承两个接口:
JpaRepository
- 封装了基本CRUD操作
JpaSpecificationExecutor
- 封装了复杂查询(分页)
- 需要提供响应的泛型
- 继承两个接口:
public interface CustomerDao extends JpaRepository<Customer, Integer>, JpaSpecificationExecutor<Customer> {}
S2.测试
@RunWith(SpringJUnit4ClassRunner.class) // 声明Spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml") // 指定Spring容器的配置信息
public class test4SpringDataJpa {
@Autowired
CustomerDao customerDao;
/**
* 根据id查询
*/
@Test
public void testFindOne() {
System.out.println("testFindOne...");
Customer customer = customerDao.findOne(2);
System.out.println(customer);
}
/**
* 保存或者更新
* 没有id => 保存
* 有id => 更新
*/
@Test
public void testSave() {
System.out.println("testSave...");
Customer customer = new Customer();
// customer.setId(2);
customer.setLastName("wangermazi");
customer.setEmail("wangermazi@qq.com");
Customer save = customerDao.save(customer);
System.out.println(save);
}
/**
* 测试删除
*/
@Test
public void testDelete() {
System.out.println("testDelete...");
customerDao.delete(16);
}
/**
* 查询所有
*/
@Test
public void testFindAll() {
System.out.println("testFindAll...");
List<Customer> all = customerDao.findAll();
for (Customer c: all) {
System.out.println(c);
}
}
}
S3.总结
findOne(id)
----> 根据id查询save(customer)
-> 保存或更新(保存:无主键;更新:有主键)delete(id)
-----> 根据id删除findAll()
------> 查询全部
C3.运行过程和原理剖析
-
Dao层只是接口,而真正发挥作用的是接口的实现类
- 在程序的执行过程中,自动的帮我们动态生成了接口的实现类对象
- 如何动态地生成实现类对象?
- 动态代理(生成基于接口的实现类对象)
-
springdatajpa的运行过程:
-
-
原理:
- 通过JdkDynamicAopProxy的invoke方法创建了一个动态代理对象
- SimpleJpaRepository当中封装了JPA的操作(借助JPA的api完成数据库的CRUD)
- 通过hibernate完成数据库操作(封装了jdbc)
P3.查询的方法
法一.JpaRepository接口中定义的方法
一、统计查询
/**
* 统计查询
*/
@Test
public void testCount() {
System.out.println("testCount...");
long count = customerDao.count();
System.out.println(count);
}
二、判断是否存在
/**
* 测试是否存在
*/
@Test
public void testExists() {
System.out.println("testExists...");
boolean exists = customerDao.exists(4);
System.out.println(exists);
}
三、根据id查询单个实体
findOne()和getOne()区别:
- findOne():
- em.find() => 立即加载
- getOne():
- em.getReference() => 延迟加载
- 延迟加载和事务有关系,所以要加上@Transactional
- 返回的是一个客户的动态代理对象
- 什么时候用,什么时候查询
-
findOne()
-
@Test public void testFindOne() { System.out.println("testFindOne..."); Customer customer = customerDao.findOne(2); System.out.println(customer); }
-
-
getOne()
-
/** * 根据id从数据库查询 * @Transactional => 使用事务 * 保证getOne正常进行 */ @Test @Transactional public void testGetOne() { System.out.println("testGetOne..."); Customer customer = customerDao.getOne(4); System.out.println(customer); }
-
四、查询所有
@Test
public void testFindAll() {
System.out.println("testFindAll...");
List<Customer> all = customerDao.findAll();
for (Customer c: all) {
System.out.println(c);
}
}
五、保存或者更新
- 没有id => 保存
- 有id => 更新
@Test
public void testSave() {
System.out.println("testSave...");
Customer customer = new Customer();
// customer.setId(2);
customer.setLastName("wangermazi");
customer.setEmail("wangermazi@qq.com");
Customer save = customerDao.save(customer);
System.out.println(save);
}
六、删除
@Test
public void testDelete() {
System.out.println("testDelete...");
customerDao.delete(16);
}
法二.JpaSpecificationExecutor接口中定义的方法
S1.方法列表
T findOne(Specification<T> spec);
- 查询单个对象
long count(Specification<T> spec);
- 统计查询
List<T> findAll(Specification<T> spec);
- 查询列表
Page<T> findAll(Specification<T> spec, Pageable pageable);
- 查询全部,分页
- pageable:分页参数
- 返回值:分页pageBean(page:是springdatajpa提供的)
List<T> findAll(Specification<T> spec, Sort sort);
- 查询列表
- Sort:排序参数
S2.Specification类
自定义我们自己的Specification实现类
- 该类通过实现
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
方法,来指明查询条件
Root
- 获取需要查询的对象属性,查询的任何属性都可以从根对象中获取
CriteriaQuery
- 顶层查询对象,自定义查询方式(了解:一般不用)
CriteriaBuilder
- 构造查询条件,内部封装了很多的查询条件
一、精确匹配查询
@Test
public void test() {
Specification<Customer> spec = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
// 1.获取比较的属性
Path<Object> lastName = root.get("lastName");
// 2.构造查询条件
Predicate predicate = criteriaBuilder.equal(lastName, "李四"); // 进行精准匹配
return predicate;
}
};
Customer customer = customerDao.findOne(spec);
System.out.println(customer);
}
二、模糊条件查询
@Test
public void testLike() {
Specification<Customer> spec = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> lastName = root.get("lastName");
Expression<String> expression = lastName.as(String.class); // 模糊查询时需要指定属性类型
Predicate predicate = criteriaBuilder.like(expression, "%ah%");
return predicate;
}
};
List<Customer> list = customerDao.findAll(spec);
for (Customer customer : list) {
System.out.println(customer);
}
}
三、对查询结果排序
@Test
public void testOrder() {
Specification<Customer> spec = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> lastName = root.get("lastName");
Expression<String> expression = lastName.as(String.class);
Predicate predicate = criteriaBuilder.like(expression, "%ah%");
return predicate;
}
};
Sort sort = new Sort(Sort.Direction.DESC, "id");
List<Customer> list = customerDao.findAll(spec, sort);
for (Customer customer : list) {
System.out.println(customer);
}
}
四、分页查询
Page<T> findAll(Specification<T> var1, Pageable var2)
=> 有条件的分页
Specification
:查询条件Pageable
:分页参数
- 查询的页码,每页的条数
- 返回:
Page
- SpringDataJpa为我们创建的PageBean对象
Page<T> findAll(Pageable var2)
=> 没有条件的分页
@Test
public void testPagging() {
Specification<Customer> spec = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> lastName = root.get("lastName");
Expression<String> expression = lastName.as(String.class);
Predicate predicate = criteriaBuilder.like(expression, "%ah%");
return predicate;
}
};
Pageable pageable = new PageRequest(0, 5);
Page<Customer> page = customerDao.findAll(spec, pageable);
for (Customer customer : page.getContent()) {
System.out.println(customer);
}
}
五、多条件查询
每一个Predicate就是一个条件
@Test
public void testMulCondition() {
Specification<Customer> spec = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> id = root.get("id");
Path<Object> lastName = root.get("lastName");
Predicate predicate0 = criteriaBuilder.equal(id, 7);
Predicate predicate1 = criteriaBuilder.equal(lastName, "haha");
Predicate predicate = criteriaBuilder.and(predicate0, predicate1);
return predicate;
}
};
Customer customer = customerDao.findOne(spec);
System.out.println(customer);
}
法三.JPQL
-
JPQL的查询方式
-
对于多个占位符参数
- 赋值的时候,默认情况下,占位符的位置需要和方法参数的位置一致
- 否则,需要指定此占位符的取值来源
?1
-
代码示例:
-
CustomerDao.java
-
public interface CustomerDao extends JpaRepository<Customer, Integer>, JpaSpecificationExecutor<Customer> { @Query(value = "from Customer where last_name=?") public List<Customer> findJpql(String lastName); @Query(value = "from Customer where id=? and lastName=?") public Customer findByNameAndIdJpql(Integer id, String lastName); /** * 对于多个占位符参数 * 赋值的时候,默认情况下,占位符的位置需要和方法参数的位置一致 * 否则的话,需要指定此占位符的取值来源 */ @Query(value = "from Customer where id=?2 and lastName=?1") public Customer findByNameAndIdOrderJpql(String lastName, Integer id); /** * 根据客户id,更新客户名称 * @Modifying => 当前是一个更新操作 */ @Query(value = "update Customer set lastName=? where id=?") @Modifying public void updateCustomerJpql(String lastName, Integer id); }
-
-
测试类
-
@RunWith(SpringJUnit4ClassRunner.class) // 声明Spring提供的单元测试环境 @ContextConfiguration(locations = "classpath:applicationContext.xml") // 指定Spring容器的配置信息 public class test4JPQL { @Autowired CustomerDao customerDao; /** * 测试Jpql,根据id查询用户 */ @Test public void testFindJpql() { List<Customer> list = customerDao.findJpql("lisi"); for (Customer customer: list) { System.out.println(customer); } } /** * 测试Jpql,根据id和名称查询客户 */ @Test public void testFindByNameAndIdJpql() { Customer customer = customerDao.findByNameAndIdOrderJpql("lisi", 3); System.out.println(customer); } /** * 测试Jpa的更新操作 * Spring Data JPA默认执行结束后会回滚事务 * @Rollback(value = false) => 设置是否自动回滚 */ @Test @Transactional @Rollback(value = false) public void testModifying() { customerDao.updateCustomerJpql("李四", 3); } }
-
-
法四.SQL
-
SQL语句的查询
-
@Query
value
:Jpql语句、Sql语句nativeQuery
:- 使用SQL查询 --> true
- 使用JPQL查询 -> false
-
返回值类型都必须为:
List<Object[]>
-
代码示例:
-
CustomerDao.java
-
public interface CustomerDao extends JpaRepository<Customer, Integer>, JpaSpecificationExecutor<Customer> { @Query(value = "select * from tbl_customer", nativeQuery = true) public List<Object[]> findSql(); @Query(value = "select * from tbl_customer where last_name like ?", nativeQuery = true) public List<Object[]> findLikeSql(String name); }
-
-
测试:
-
@RunWith(SpringJUnit4ClassRunner.class) // 声明Spring提供的单元测试环境 @ContextConfiguration(locations = "classpath:applicationContext.xml") // 指定Spring容器的配置信息 public class test4SQL { @Autowired CustomerDao customerDao; @Test public void testFind() { System.out.println("testFind..."); List<Object[]> objects = customerDao.findSql(); for (Object[] obj: objects) { System.out.println(Arrays.toString(obj)); } } @Test public void testFuzzyQuery() { System.out.println("testFuzzyQuery..."); List<Object[]> list = customerDao.findLikeSql("%is%"); for (Object[] obj: list) { System.out.println(Arrays.toString(obj)); } } }
-
-
法五.方法名称规则查询
-
对jpql查询更加深入一层的封装
-
我们只需要按照SpringDataJpa提供的方法名称规则定义方法,不需要再去配置jpql语句,完成查询
-
约定:
findBy+属性名(对象,首字母大写)
- 如:
findByLastName
- 在Spring Data Jpa的运行阶段,会根据方法名称进行解析:
- findBy => from xxx(实体类)
- 属性名称 => where 属性名=
- 根据属性名称进行完全匹配
- 如:
findBy+属性名+查询方式
- 如:
findByLastNameLike
- 如:
findBy+属性名+[查询方式]+And|Or+属性名+[查询方式]
-
代码示例:
-
Customer.java
-
public interface CustomerDao extends JpaRepository<Customer, Integer>, JpaSpecificationExecutor<Customer> { public List<Customer> findByLastName(String lastName); public List<Customer> findByLastNameLike(String lastName); public List<Customer> findByLastNameLikeOrEmail(String lastName, String email); }
-
-
测试:
-
@RunWith(SpringJUnit4ClassRunner.class) // 声明Spring提供的单元测试环境 @ContextConfiguration(locations = "classpath:applicationContext.xml") // 指定Spring容器的配置信息 public class test4NameRule { @Autowired CustomerDao customerDao; @Test public void testQueryByLastName() { System.out.println("testQueryByLastName..."); List<Customer> list = customerDao.findByLastName("lisi"); for (Customer c: list) { System.out.println(c); } } @Test public void testFindByLastNameLike() { System.out.println("testFindByLastNameLike..."); List<Customer> list = customerDao.findByLastNameLike("%ah%"); for (Customer customer : list) { System.out.println(customer); } } @Test public void testFindByLastNameLikeOrEmail() { System.out.println("testFindByLastNameLikeOrEmail..."); List<Customer> list = customerDao.findByLastNameLikeOrEmail("%四%", "wangermazi@qq.com"); for (Customer customer : list) { System.out.println(customer); } } }
-
-
P4.多表
C1.多表之间的关系
-
表关系
- 一对一
- 一对多
- 一 => 主表
- 多 => 从表
- 外键:在从表上
- 多对多
- 中间表
-
实体类中的关系
- 包含关系
- 继承关系
-
分析步骤
- 明确表关系
- 确定表关系
- 编写实体类
- 配置映射关系
C2.多表操作
S1.一对多
一、普通操作代码示例
配置外键的过程,配置到多的一方,就会由多的一方维护外键;配置到少的一方,就会由少的一方维护外键
两边都配置 => 双向配置
只配置一边 => 单向配置由一的一方维护外键,会发送update语句,所以一般一的一方会放弃维护权
使用注解的形式配置多表关系
- 声明关系
- @OneToMany
- targetEntity => 对方对象的class
- mappedBy => 指定参考多一方的哪一个属性
- cascade => 指定级联的范围
- ALL => Cascade all operations
- PERSIST => Cascade persist operation
- MERGE => Cascade merge operation
- REMOVE => Cascade remove operation
- REFRESH => Cascade refresh operation
- DETACH => Cascade detach operation
- @ManyToOne => 配置多对一关系
- 配置关系(中间表)
- @JoinColumn => 配置外键
- name => 外键字段名称
- referencedColumnName => 主表中的主键字段名称
配置外键的过程,配置到One的一方,就会在One的一方维护外键
-
实体类
-
@Entity @Table(name = "tbl_company") public class Company { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "company_id") private Integer companyId; @Column(name = "last_name", length = 50, nullable = false) private String lastName; @Column(length = 50) private String email; /* 配置一对多关系 */ // @OneToMany(targetEntity = Employee.class) // @JoinColumn(name = "c_id", referencedColumnName = "company_id") @OneToMany(mappedBy = "company") // 一的一方放弃维护外键 private Set<Employee> employees = new HashSet<Employee>(); // 略 }
-
@Entity @Table(name = "tbl_employee") public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "employee_id") private Integer employeeId; @Column(name = "last_name", length = 50, nullable = false) private String lastName; @Column private Integer age; /* 配置多对一的关系 */ @ManyToOne(targetEntity = Company.class) @JoinColumn(name = "c_id", referencedColumnName = "company_id") private Company company; // 略 }
-
-
OneToManyTest.java
-
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class OneToManyTest { @Autowired private CompanyDao companyDao; @Autowired private EmployeeDao employeeDao; /** * 保存一个客户,保存一个联系人 */ @Test @Transactional @Rollback(false) public void testAdd() { Company company = new Company(); company.setLastName("百度"); Employee employee = new Employee(); employee.setLastName("小李"); employee.setCompany(company); company.getEmployees().add(employee); companyDao.save(company); employeeDao.save(employee); } }
-
-
pom.xml
-
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <groupId>xqy.maven.jpa</groupId> <artifactId>jjpa-demo5-onetomany</artifactId> <version>1.0-SNAPSHOT</version> <modelVersion>4.0.0</modelVersion> <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-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> <!-- 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>
-
-
applicationContext.xml
-
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" 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/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/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <!-- Spring 和 Spring Data JPA的配置 --> <!-- 1.创建entityManagerFactory对象交给Spring容器管理 --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <!-- 配置数据源 --> <property name="dataSource" ref="dataSource"/> <!-- 配置扫描的包(实体类所在的包) --> <property name="packagesToScan" value="xqy.bean"/> <!-- JPA的实现厂家 --> <property name="persistenceProvider"> <bean class="org.hibernate.jpa.HibernatePersistenceProvider"></bean> </property> <!-- JPA供应商的适配器 --> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <!--配置是否自动创建数据库表 --> <property name="generateDdl" value="false"/> <!--指定数据库类型 --> <property name="database" value="MYSQL"/> <!--数据库方言:支持的特有语法 --> <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/> <!--是否显示sql --> <property name="showSql" value="true"/> </bean> </property> <!-- JPA的方言(可选):高级特性 --> <property name="jpaDialect"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean> </property> <!-- 注入jpa的配置信息 加载jpa的基本配置信息和jpa实现方式(hibernate)的配置信息 hibernate.hbm2ddl.auto => 自动创建数据库表 create => 每次都会重新创建数据库表 update => 有表不会重新创建,没有表会重新创建表 --> <property name="jpaProperties"> <props> <prop key="hibernate.hbm2ddl.auto">create</prop> </props> </property> </bean> <!-- 2.创建数据库连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="root"></property> <property name="password" value="123456"></property> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://192.168.252.128:3306/jpa?useUnicode=true&characterEncoding=utf8"/> </bean> <!-- 3.整合Spring Data JPA --> <jpa:repositories base-package="xqy.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.声明式事务 --> <!-- 6.配置包扫描 --> <context:component-scan base-package="xqy"></context:component-scan> </beans>
-
二、级联操作代码示例
-
Employee.java
-
@Entity @Table(name = "tbl_employee") public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "employee_id") private Integer employeeId; @Column(name = "last_name", length = 50, nullable = false) private String lastName; @Column private Integer age; /* 配置多对一的关系 */ @ManyToOne(targetEntity = Company.class) @JoinColumn(name = "c_id", referencedColumnName = "company_id") private Company company; // 略 }
-
-
Company.java
-
@Entity @Table(name = "tbl_company") public class Company { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "company_id") private Integer companyId; @Column(name = "last_name", length = 50, nullable = false) private String lastName; @Column(length = 50) private String email; /* 配置一对多关系 */ @OneToMany(mappedBy = "company", cascade = CascadeType.ALL) // 配置级联 private Set<Employee> employees = new HashSet<Employee>(); // 略 }
-
-
OneToManyTest.java
-
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class OneToManyTest { @Autowired private CompanyDao companyDao; @Autowired private EmployeeDao employeeDao; /** * 级联添加 */ @Test @Transactional @Rollback(false) public void testCascadeAdd() { Company company = new Company(); company.setLastName("阿里3"); Employee employee = new Employee(); employee.setLastName("二狗子3"); Employee employee2 = new Employee(); employee2.setLastName("小李3"); // 设置好双方的关系 company.getEmployees().add(employee); employee.setCompany(company); company.getEmployees().add(employee2); employee2.setCompany(company); companyDao.save(company); // 不用再手动保存employee,employee会自动保存到数据库中 } /** * 级联删除 */ @Test @Transactional @Rollback(false) public void testCascadeRemove() { Company company = companyDao.getOne(3); companyDao.delete(company); } }
-
S2.多对多
- 配置和“一对多”的配置一样
- 多对多的某一方必须放弃维护权,否则会报错
- @ManyToMany
- targetEntity
- cascade
- mappedBy => 参照对方实体中哪个属性的配置
- @JoinTable => 配置中间表
- name => 中间表的名称
- joinColumns => 配置当前对象在中间表中的外键
- @JoinColumn
- name => 中间表中的字段名
- referencedColumnName => 实体中对应的属性名
- inverseJoinColumns => 配置对方对象在中间表中的外键
- @JoinColumn
- name => 中间表中的字段名
- referencedColumnName => 实体中对应的属性名
一、普通操作代码示例
-
Person.java
-
@Entity @Table(name = "tbl_person") public class Person { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "person_id") private Integer personId; @Column(name = "name") private String name; /* 配置多对多关系的映射关系 */ @ManyToMany(targetEntity = Job.class) @JoinTable(name = "sys_person_job", joinColumns = {@JoinColumn(name = "p_id", referencedColumnName = "person_id")}, inverseJoinColumns = {@JoinColumn(name = "j_id", referencedColumnName = "job_id")} ) private Set<Job> jobs = new HashSet<Job>(); // 略 }
-
-
Job.java
-
@Entity @Table(name = "tbl_job") public class Job { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "job_id") private Integer jobId; @Column(name = "name") private String name; /* 配置多对多关系的映射关系 */ @ManyToMany(mappedBy = "jobs") // 参照对方实体中哪个属性的配置 private Set<Person> people = new HashSet<Person>(); // 略 }
-
-
测试类:
-
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class ManyToManyTest { @Autowired private PersonDao personDao; @Autowired private JobDao jobDao; @Test @Transactional @Rollback(false) public void testAdd() { System.out.println("testAdd..."); Person person = new Person(); person.setName("李四"); Job job = new Job(); job.setName("工程师"); person.getJobs().add(job); personDao.save(person); jobDao.save(job); } }
-
二、级联操作代码示例
-
Person.java
-
@Entity @Table(name = "tbl_person") public class Person { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "person_id") private Integer personId; @Column(name = "name") private String name; /* 配置多对多关系的映射关系 */ @ManyToMany(targetEntity = Job.class, cascade = CascadeType.ALL) @JoinTable(name = "sys_person_job", joinColumns = {@JoinColumn(name = "p_id", referencedColumnName = "person_id")}, inverseJoinColumns = {@JoinColumn(name = "j_id", referencedColumnName = "job_id")} ) private Set<Job> jobs = new HashSet<Job>(); // 略 }
-
-
Job.java
-
@Entity @Table(name = "tbl_job") public class Job { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "job_id") private Integer jobId; @Column(name = "name") private String name; /* 配置多对多关系的映射关系 */ @ManyToMany(mappedBy = "jobs") private Set<Person> people = new HashSet<Person>(); // 略 }
-
-
测试类:
-
/** * 另外一张表、中间表中的对应元组都会被删除 */ @Test public void testCascadeDelete() { System.out.println("testCascadeDelete..."); Person person = personDao.getOne(1); personDao.delete(person); }
-
S3.对象导航查询
一、从一查多
- 查询一个对象的时候,通过此对象查询所有的关联对象
- 从一查多默认使用的是延迟加载
- 若不想延迟加载,则可以修改配置:
- 在
OneToMany
或ManyToOne
或ManyToMany
上配置fetch
FetchType.EAGER
=> 立即查询(不推荐)FetchType.LAZY
=> 懒加载
@Test
@Transactional
@Rollback(false)
public void testQuery1() {
Company company = companyDao.getOne(2);
Set<Employee> employees = company.getEmployees();
for (Employee employee : employees) {
System.out.println(employee);
}
}
二、从多查一
- 从多查一默认使用立即加载
@Test
@Transactional
@Rollback(false)
public void testQuery2() {
Employee employee = employeeDao.findOne(2);
System.out.println(employee.getCompany());
}