jpa与springdata jpa快速上手
1、jpa
1.1、orm思想
主要目的:
操作实体类就相当于操作数据库表
建立两个映射关系:
- 实体类和表的映射关系
- 实体类中属性和表字段的映射关系
不再重点关注sql语句
实现了orm思想的框架有:mybatis、hibernate
1.2、hibernate框架
介绍:JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
Sun引入新的JPA ORM规范出于两个原因:其一,简化现有Java EE和Java SE应用开发工作;其二,Sun希望整合ORM技术,实现天下归一。
1.3、JPA规范
概念:Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
- 由接口和抽象类组成,不能干活。
- 它的实现有:hibernate、toplink
1.4、jpa入门案例
案例:对客户的相关操作(基本的增、删、改、查)
环境搭建
- 创建工程,导入坐标
- 配置jpa核心配置文件:配置要META-INF的文件夹下,且文件名为persistence.xml
- 编写客户实体类
- 配置实体类和表、类中属性和表字段对应关系
坐标:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!--
name:持久化单元名称
transaction-type:事务管理方式
resource_local:本地事务管理,数据库在本地
JTA:分布式事务管理
-->
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<!--jpa的实现方式-->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 数据库信息-->
<properties>
<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>
<!-- 可选配置,jpa实现方的配置-->
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"></property>
<!--显示sql-->
<property name="hibernate.show_sql" value="true"></property>
<!--hibernate.hbm2ddl.auto:自动创建数据库表
create:程序运行时创建数据库表(有表的话先删除后创建)
update:程序运行时创建数据库表(有表的话则不创建)
none:不创建表
-->
<property name="hibernate.hbm2ddl.auto" value="update"></property>
</properties>
</persistence-unit>
</persistence>
编写实体类Customer.java
package cn.itcast.domain;
import javax.persistence.*;
@Entity//声明此类是实体类
@Table(name = "cst_customer")//配置实体类和表的映射关系
public class Customer {
@Id//声明主键的配置
//主键生成策略。IDENTITY:自增(底层数据库必须支持自动增长方式),mysql
// SEQUENCE:系列(底层数据库必须支持),oracle
// TABLE:jpa提供的一种机制,通过一张表的形式帮助我们完成自增
// AUTO:由程序自动的帮助我们选择主键生成策略
@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_level")
private String custLevel;
@Column(name = "cust_industry")
private String custIndustry;
@Column(name = "cust_phone")
private String custPhone;
@Column(name = "cust_address")
private String custAddress;
public Long getCustId() {
return custId;
}
public void setCustId(Long custId) {
this.custId = custId;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
public String getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custLevel='" + custLevel + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custPhone='" + custPhone + '\'' +
", custAddress='" + custAddress + '\'' +
'}';
}
}
测试
步骤:
- 加载配置文件创建工厂(实体类管理器工厂)对象
- 通过实体类管理器工厂获取实体管理器
- 获取事务对象,开启事务
- 完成增删改查操作
- 提交事务(回滚事务)
- 释放资源
package com.itcast;
import cn.itcast.domain.Customer;
import cn.itcast.utils.JpaUtils;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JpaTest {
@Test
public void testSave(){
//1、加载配置文件创建工厂(实体类管理器工厂)对象
//EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
//2、通过实体类管理器工厂获取实体管理器(这里我使用了工具类直接获得)
//EntityManager em = factory.createEntityManager();
EntityManager em = JpaUtils.getEntityManager();
//3、获取事务对象,开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//4、保存
Customer customer = new Customer();
customer.setCustName("xt");
customer.setCustAddress("beijing");
em.persist(customer);
//5、提交
tx.commit();
//6、释放资源,如果使用了实体类管理器工厂也需要关闭
em.close();
//factory.close();
}
@Test
public void testFind(){
EntityManager em = JpaUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//Customer customer = em.find(Customer.class, 1L);//立即加载,根据ID查询对象。
Customer customer = em.getReference(Customer.class, 1L);//会使用延迟加载机制
System.out.println(customer);//使用getRefeference的话,要将使用的对象,放在事务提交里面,否则会出异常(因为使用的是延迟加载,如果放在提交后面,事务已经提交,执行sql就会出错。)
tx.commit();
em.close();
}
@Test
public void testRemove(){
EntityManager em = JpaUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer customer = em.find(Customer.class, 2L);
em.remove(customer);
tx.commit();
em.close();
}
@Test
public void testMerge(){
EntityManager em = JpaUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer customer = em.find(Customer.class, 1L);
customer.setCustPhone("110");
em.merge(customer);
tx.commit();
em.close();
}
}
工具类代码
package cn.itcast.utils;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class JpaUtils {
private static EntityManagerFactory factory;
static {
factory = Persistence.createEntityManagerFactory("myJpa");
}
public static EntityManager getEntityManager(){
return factory.createEntityManager();
}
}
这里有几个对象很重要
- Persistence:使用静态方法createEntityManagerFactory创建实体类管理器工厂
- EntityManagerFactory:使用createEntityManager()方法获取EntityManager对象;这个工厂对象内部维护了很多内容:缓存信息、所有实体类管理器对象、在创建工厂过程中会根据配置创建数据库表;特点:线程安全的对象,多个线程访问同一个EntityManagerFactory不会有线程安全问题。但是创建过程比较浪费资源。如何解决这个问题呢?可以使用静态代码块创建EntityManagerFactory。
总结增、删、改、查方法:
- 查:
- find(Class class,T key),使用立即加载,参数为查询实体类的字节码、主键对应的实体类字段;
- getReference(Class class,T key),使用延迟加载,参数为查询实体类的字节码、主键对应的实体类字段。调用该方法不会立即发送sql语句查询数据库,什么时候用,什么时候查询数据库。
- 删:remove(Object o),传入要删除的对象实体类,该对象实体类主键必须要有值。
- 改:merge(Object o),传入要更新的对象,对象主键字段需要赋值。
- 增:persist(Object o),传入一个要增加的对象,如果设置了主键自增策略,此处可以不用传入主键值。
1.5、jpql查询
面向对象的一种查询方式,里面封装了一些高级查询方法。查询的是表和表中的字段。具体方法和操作步骤如下:
- 查询所有
@Test
public void QueryAll(){
//1、获取实体类管理器对象
EntityManager entityManager = JpaUtils.getEntityManager();
//2、获取事务管理器,开启事务
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//3、查询全部
String ql = "from Customer";//jpql不支持select *,只需要写后面部分就可以
//3.1、创建Query对象,Query对象才是执行jpql对象
Query query = entityManager.createQuery(ql);
//3.2、query.getResultList(),发送查询,并封装结果集
List<Customer> list = query.getResultList();
list.forEach(System.out::println);
tx.commit();
}
- 排序查询order by
@Test
public void QueryAll(){
//1、获取实体类管理器对象
EntityManager entityManager = JpaUtils.getEntityManager();
//2、获取事务管理器,开启事务
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//3、查询全部
String ql = "from Customer order by custId desc";//倒序查询全部
//3.1、创建Query对象,Query对象才是执行jpql对象
Query query = entityManager.createQuery(ql);
//3.2、query.getResultList(),发送查询,并封装结果集
List<Customer> list = query.getResultList();
list.forEach(System.out::println);
tx.commit();
}
- 聚合函数count()查询
@Test
public void QueryCount(){
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
String ql = "select count(custId) from Customer";
Query query = entityManager.createQuery(ql);
Object result = query.getSingleResult();//获取返回结果
System.out.println(result);
tx.commit();
}
- 分页查询
@Test
public void QueryPages(){
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
String jpql = "from Customer";
Query query = entityManager.createQuery(jpql);
query.setFirstResult(0);//开头记录的ID
query.setMaxResults(1);//需要查询的个数
Object result = query.getResultList();
System.out.println(result);
tx.commit();
}
- 模糊查询like
步骤:
- 编写jpql
- 给jpql参数赋值:query.setParameter(1,“x%”),参数索引从1开始
- 执行语句,获取结果集
@Test
public void QueryLike(){
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
String jpql = "from Customer where custName like ?";//1、模糊查询jpql语句,查询的是表和表中字段,参数使用占位符?
Query query = entityManager.createQuery(jpql);
query.setParameter(1,"x%");//2、给占位符赋值
Object result = query.getResultList();//3、执行语句,获取结果集
System.out.println(result);
tx.commit();
}
2、springdata jpa
2.1、介绍
springdata jpa 封装了jpa规范,本身也还是规范,并不能真正的干活,还需要有实现才能干活,如:hibernate
2.2、快速入门
- 创建工程、导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xtao</groupId>
<artifactId>springdata-jpa</artifactId>
<version>1.0-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>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--spring 包-->
<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>
<!--spring对orm框架的支持包-->
<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结束-->
<!--hibernate包-->
<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结束-->
<!--c3p0-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.9.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring jpa必须要求导的-->
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
</dependencies>
</project>
- 配置spring的配置文件applicationContext.xml,放在resource下面
<?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
https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!--扫描的包,实体类所在的包-->
<property name="packagesToScan" value="cn.itcast.domain"></property>
<!--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>
<!---->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"></property>
<!---->
<property name="showSql" value="true"></property>
</bean>
</property>
<!--jpa方言,高级特性-->
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean>
</property>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value="root"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///jpa"></property>
</bean>
<!--整合springdata jpa 对此包下动态代理-->
<jpa:repositories base-package="cn.itcast.dao" transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<context:component-scan base-package="cn.itcast"></context:component-scan>
</beans>
- 编写实体类,使用jpa注解配置映射关系
package cn.itcast.domain;
import javax.persistence.*;
@Entity//实体类
@Table(name = "cst_customer")//实体类和表的映射关系
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cust_id")
private Long custId;
@Column(name = "cust_name")
private String custName;
@Column(name = "cust_source")
private String custSource;
@Column(name = "cust_level")
private String custLevel;
@Column(name = "cust_industry")
private String custIndustry;
@Column(name = "cust_phone")
private String custPhone;
@Column(name = "cust_address")
private String custAddress;
public Long getCustId() {
return custId;
}
public void setCustId(Long custId) {
this.custId = custId;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
public String getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custLevel='" + custLevel + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custPhone='" + custPhone + '\'' +
", custAddress='" + custAddress + '\'' +
'}';
}
}
- 编写springdatajpa的dao层接口
package cn.itcast.dao;
import cn.itcast.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;
public interface CustomerDao extends JpaRepository<Customer,Long>,JpaSpecificationExecutor<Customer> {
}
- 只要直接继承两个接口就有操作数据库的基本方法
- 测试
package cn.itcast;
import cn.itcast.dao.CustomerDao;
import cn.itcast.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 springdataJpaTest {
@Autowired
CustomerDao customerDao;
@Test
public void queryFindOne(){
Customer customer = customerDao.findOne(1l);//立即加载
System.out.println(customer);
}
@Test
public void testSave(){
Customer customer = new Customer();
customer.setCustId(3l);
customer.setCustName("lj");
Customer save = customerDao.save(customer);//保存或更新;如果存在该对象,就会执行更新,没有就执行保存
System.out.println(save);
}
@Test
public void testRemove(){
customerDao.delete(3l);
}
@Test
public void testFindAll(){
List<Customer> customers = customerDao.findAll();
System.out.println(customers);
}
@Test
public void testCount(){
long count = customerDao.count();
System.out.println(count);
}
@Test
public void testExists(){
boolean exists = customerDao.exists(4l);
System.out.println(exists);
}
@Test
@Transactional//保证getOne方法正常执行,延迟加载和事务有关系
public void testGetOne(){
Customer one = customerDao.getOne(4l);//延迟加载
//System.out.println(one);
}
}
save()方法既可以保存,也可以更新,当用户id存在时就更新,不存在则保存。
2.2、原理剖析
- 通过JdkDynamicAopProxy的invoke方法创建了一个动态代理对象
- 代理对象SimpleJpaRepository当中封装了JPA的操作
- 通过hibernate完成数据库的操作
2.3、springjpadata的jpql操作
在dao接口上添加对应方法
@Query(value = "from Customer where custName = ?")
public Customer findByName(String name);
//?后面加索引可以设置取值来源,如?2
@Query(value = "from Customer where custName = ?1 and custId = ?2")
public Customer findByNameAndId(String name,Long id);
@Query(value = "update Customer set custName = ?2 where custId = ?1")
@Modifying//表示这是个更新操作
public void update(Long id,String name);
//true代表sql查询,false代表jpql查询
@Query(nativeQuery = true,value = "select * from cst_customer")
public List<Object[]> findSql();
@Query(nativeQuery = true,value = "select * from cst_customer where cust_name like ?")
public List<Object[]> findSqlLike(String name);
使用
@Autowired
CustomerDao customerDao;
//自定义方法,使用@Query
@Test
public void findByName(){
Customer customer = customerDao.findByName("lj");
System.out.println(customer);
}
@Test
public void findByNameAndId(){
Customer customer = customerDao.findByNameAndId("lj",4l);
System.out.println(customer);
}
@Test
@Transactional
@Rollback(value = false)
public void testUpdate(){
customerDao.update(4l, "lj6");
}
@Test
public void findSql(){
List<Object[]> all = customerDao.findSql();
for (Object[] o : all){
System.out.println(Arrays.toString(o));
}
}
@Test
public void findSqlLike(){
List<Object[]> all = customerDao.findSqlLike("%xiaotao%");
for (Object[] o : all){
System.out.println(Arrays.toString(o));
}
}
2.4、根据参数名称自定义方法
说明:根据规则按照实体类属性名称自定义方法可以不需要写jpql或sql语句
代码
//方法名的约定,只要根据规定取方法名,就不需要写jpql
public Customer findByCustName(String name);
//使用方法命名规则模糊查询
public Customer findByCustNameLike(String name);
//使用方法命名规则多条件模糊查询
public Customer findByCustNameAndCustId(String name,Long id);
使用
@Test
public void findByCustName(){
Customer customer = customerDao.findByCustName("xiaotao");
System.out.println(customer);
}
@Test
public void findByCustNameLike(){
Customer customer = customerDao.findByCustNameLike("xiao%");
System.out.println(customer);
}
@Test
public void findByCustNameAndCustId(){
Customer customer = customerDao.findByCustNameAndCustId("xiaotao",1l);
System.out.println(customer);
}
2.5、使用Specification高级查询
步骤1、创建Specification对象,实现对象接口中未实现的方法
步骤2、在实现方法中传入需要查询的内容
步骤3、将对象传入查询方法
package cn.itcast;
import cn.itcast.dao.CustomerDao;
import cn.itcast.domain.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.persistence.criteria.*;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class SpecificationTest {
@Autowired
CustomerDao customerDao;
@Test
public void findOne(){
Specification<Customer> specification = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> custName = root.get("custName");
Predicate predicate = criteriaBuilder.equal(custName, "xiaotao");
return predicate;
}
};
Customer customer = customerDao.findOne(specification);
System.out.println(customer);
}
@Test
public void findOnes(){
Specification<Customer> specification = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> custName = root.get("custName");
Path<Object> custId = root.get("custId");
Predicate predicate = criteriaBuilder.equal(custName, "xiaotao");
Predicate predicate1 = criteriaBuilder.equal(custId,1l);
Predicate and = criteriaBuilder.and(predicate, predicate1);
return and;
}
};
Customer customer = customerDao.findOne(specification);
System.out.println(customer);
}
@Test
public void findLike(){
Specification<Customer> specification = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> custName = root.get("custName");
//对于gt,lt,ge,le,like:得到path对象,根据path指定比较的参数类型,再去进行比较;指定参数类型:path.as()
Predicate predicate = criteriaBuilder.like(custName.as(String.class),"xiao%");
return predicate;
}
};
Customer customer = customerDao.findOne(specification);
System.out.println(customer);
}
@Test
public void findOrderBy(){
Specification<Customer> specification = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> custName = root.get("custName");
Predicate predicate = criteriaBuilder.equal(custName,"xiaotao");
return predicate;
}
};
Sort sort = new Sort(Sort.Direction.DESC,"custId");
List<Customer> customers = customerDao.findAll(specification,sort);
System.out.println(customers);
}
@Test
public void findPages(){
Specification<Customer> specification = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> custName = root.get("custName");
Predicate predicate = criteriaBuilder.equal(custName,"xiaotao");
return predicate;
}
};
Pageable pageable = new PageRequest(0,1);
Page<Customer> customers = customerDao.findAll(specification, pageable);
List<Customer> custs = customers.getContent();
System.out.println(custs);
}
}
3、一对多
3.1、使用
- 创建实体类Customer(一)和LinkMan(多)
package cn.itcast.domain;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity//实体类
@Table(name = "cst_customer")//实体类和表的映射关系
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cust_id")
private Long custId;
@Column(name = "cust_name")
private String custName;
@Column(name = "cust_source")
private String custSource;
@Column(name = "cust_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;
@OneToMany(targetEntity = LinkMan.class)
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private Set<LinkMan> linkMans = new HashSet<>();
public Long getCustId() {
return custId;
}
public void setCustId(Long custId) {
this.custId = custId;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
public String getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
public Set<LinkMan> getLinkMans() {
return linkMans;
}
public void setLinkMans(Set<LinkMan> linkMans) {
this.linkMans = linkMans;
}
@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custLevel='" + custLevel + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custPhone='" + custPhone + '\'' +
", custAddress='" + custAddress + '\'' +
", linkMans=" + linkMans +
'}';
}
}
package cn.itcast.domain;
import javax.persistence.*;
@Entity
@Table(name = "cst_linkman")
public class LinkMan {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "lkm_id")
private Long lkmId;
@Column(name = "lkm_name")
private String lkmName;
@Column(name = "lkm_gender")
private String lkmGender;
@Column(name = "lkm_phone")
private String lkmPhone;
@Column(name = "lkm_mobile")
private String lkmMobile;
@Column(name = "lkm_email")
private String lkmEmail;
@Column(name = "lkm_position")
private String lkmPosition;
@Column(name = "lkm_memo")
private String lkmMemo;
@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private Customer customer;
public Long getLkmId() {
return lkmId;
}
public void setLkmId(Long lkmId) {
this.lkmId = lkmId;
}
public String getLkmName() {
return lkmName;
}
public void setLkmName(String lkmName) {
this.lkmName = lkmName;
}
public String getLkmGender() {
return lkmGender;
}
public void setLkmGender(String lkmGender) {
this.lkmGender = lkmGender;
}
public String getLkmPhone() {
return lkmPhone;
}
public void setLkmPhone(String lkmPhone) {
this.lkmPhone = lkmPhone;
}
public String getLkmMobile() {
return lkmMobile;
}
public void setLkmMobile(String lkmMobile) {
this.lkmMobile = lkmMobile;
}
public String getLkmEmail() {
return lkmEmail;
}
public void setLkmEmail(String lkmEmail) {
this.lkmEmail = lkmEmail;
}
public String getLkmPosition() {
return lkmPosition;
}
public void setLkmPosition(String lkmPosition) {
this.lkmPosition = lkmPosition;
}
public String getLkmMemo() {
return lkmMemo;
}
public void setLkmMemo(String lkmMemo) {
this.lkmMemo = lkmMemo;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
@Override
public String toString() {
return "LinkMan{" +
"lkmId=" + lkmId +
", lkmName='" + lkmName + '\'' +
", lkmGender='" + lkmGender + '\'' +
", lkmPhone='" + lkmPhone + '\'' +
", lkmMobile='" + lkmMobile + '\'' +
", lkmEmail='" + lkmEmail + '\'' +
", lkmPosition='" + lkmPosition + '\'' +
", lkmMemo='" + lkmMemo + '\'' +
", customer=" + customer +
'}';
}
}
- 创建对应dao接口
package cn.itcast.dao;
import cn.itcast.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;
public interface CustomerDao extends JpaRepository<Customer,Long>,JpaSpecificationExecutor<Customer> {
}
package cn.itcast.dao;
import cn.itcast.domain.Customer;
import cn.itcast.domain.LinkMan;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface LinkManDao extends JpaRepository<LinkMan,Long>,JpaSpecificationExecutor<LinkMan> {
}
- 测试
package cn.itcast;
import cn.itcast.dao.CustomerDao;
import cn.itcast.dao.LinkManDao;
import cn.itcast.domain.Customer;
import cn.itcast.domain.LinkMan;
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 java.util.HashSet;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class OneToManyTest {
@Autowired
CustomerDao customerDao;
@Autowired
LinkManDao linkManDao;
@Test
@Transactional
@Rollback(value = false)
public void save(){
Customer customer = new Customer();
customer.setCustName("李白");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("韩信");
customer.getLinkMans().add(linkMan);
Customer customer1 = customerDao.save(customer);
LinkMan linkMan1 = linkManDao.save(linkMan);
System.out.println(customer1);
System.out.println(linkMan1);
}
}
3.2、放弃外键维护
注意:
这里一的一方对外键进行了维护,所以当一的一方添加多的一方的时候会对外键进行更新操作,会多出一条sql语句;而如果是多的一方添一的一方,则会直接对外键和其他数据一同保存,少一条语句。要使得一的一方不会有多余的sql语句,需要对外键放弃维护,实现方式如下:
//@OneToMany(targetEntity = LinkMan.class)
//@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
@OneToMany(mappedBy = "customer")
private Set<LinkMan> linkMans = new HashSet<>();
只需用mappedBy属性指明对方外键维护的属性即可。这样即使是一的一方添加多的一方也不会有多余的语句。不过这里有一点需要注意:放弃维护了的话,多方必须要添加一方才会对外键赋值,不然外键仍然没有值。
3.3、删除
- 默认(一的一方对外键进行了维护)
如果要删除一的一方,会将多的一方外键置为空
- 放弃维护(一方放弃维护外键)
这时将不能删除一方,会出错,因为外键被占用,而一方又因为不能维护所以不能修改。
3.4、级联
3.4.1、级联添加
当一方保存了多方,或多方保存了一方,只需保存一个就可以把所有都保存进去,这就是级联添加。
实现:
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)
private Set<LinkMan> linkMans = new HashSet<>();
只要在一方的一对多注解上配置cascade属性,设置所有操作级联即可。
@Test
@Transactional
@Rollback(value = false)
public void save(){
Customer customer = new Customer();
customer.setCustName("xt");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("sj");
linkMan.setCustomer(customer);
customer.getLinkMans().add(linkMan);
Customer customer1 = customerDao.save(customer);
//LinkMan linkMan1 = linkManDao.save(linkMan);
System.out.println(customer1);
//System.out.println(linkMan1);
}
3.4.2、级联删除
删除一的一方通过级联删除,可以把多的一方都删除。实现方式和普通删除是一样的,只需配置级联属性即可。
4、多对多
4.1、配置
@ManyToMany(targetEntity = LinkMan.class)
@JoinTable(name = "sys_cust_lkman",
joinColumns = {@JoinColumn(name = "sys_cust_id",referencedColumnName = "cust_id")},
inverseJoinColumns = {@JoinColumn(name = "sys_lkm_id",referencedColumnName = "lkm_id")}
)
private Set<LinkMan> linkMans = new HashSet<>();
@ManyToMany(targetEntity = Customer.class)
@JoinTable(name = "sys_cust_lkman",
joinColumns = {@JoinColumn(name = "sys_lkm_id",referencedColumnName = "lkm_id")},
inverseJoinColumns = {@JoinColumn(name = "sys_cust_id",referencedColumnName = "cust_id")}
)
测试
@Test
@Transactional
@Rollback(value = false)
public void save1(){
Customer customer = new Customer();
customer.setCustName("123");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("123");
linkMan.getCustomers().add(customer);
//customer.getLinkMans().add(linkMan);
Customer customer1 = customerDao.save(customer);
LinkMan linkMan1 = linkManDao.save(linkMan);
System.out.println(customer1);
//System.out.println(linkMan1);
}
这里有一个地方需要注意,如果双方都进行了把对方添加到自己属性里,那么就会出现主键冲突,因为双方都对外键进行维护,保存的值都是一样的,就会出错,所以就要有一方放弃维护。
@ManyToMany(mappedBy = "linkMans")
private Set<Customer> customers = new HashSet<>();
这样就不会有冲突了。
4.2、级联
在保存的一方配置cascade属性
@ManyToMany(targetEntity = LinkMan.class,cascade = CascadeType.ALL)
@JoinTable(name = "sys_cust_lkman",
joinColumns = {@JoinColumn(name = "sys_cust_id",referencedColumnName = "cust_id")},
inverseJoinColumns = {@JoinColumn(name = "sys_lkm_id",referencedColumnName = "lkm_id")}
)
private Set<LinkMan> linkMans = new HashSet<>();
测试
@Test
@Transactional
@Rollback(value = false)
public void save1(){
Customer customer = new Customer();
customer.setCustName("12345");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("12345");
linkMan.getCustomers().add(customer);
customer.getLinkMans().add(linkMan);
Customer customer1 = customerDao.save(customer);
//LinkMan linkMan1 = linkManDao.save(linkMan);//依然会被保存
System.out.println(customer1);
//System.out.println(linkMan1);
}
5、对象导航查询
介绍
查询一个对象,会将它所关联的对象全部查询出来。
Customer customer = customerDao.getOne(24l);
Set<LinkMan> linkMans = customer.getLinkMans();
System.out.println(linkMans);
在注解上配置延迟加载或立即加载。
默认一的一方查询多的一方使用延迟加载,多的一方查询一的一方使用立即加载。默认指的是不配置的情况下。
@ManyToMany(targetEntity = LinkMan.class,cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@JoinTable(name = "sys_cust_lkman",
joinColumns = {@JoinColumn(name = "sys_cust_id",referencedColumnName = "cust_id")},
inverseJoinColumns = {@JoinColumn(name = "sys_lkm_id",referencedColumnName = "lkm_id")}
)
private Set<LinkMan> linkMans = new HashSet<>();