jpa和springdatajpa

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 + '\'' +
                '}';
    }
}

测试

步骤:

  1. 加载配置文件创建工厂(实体类管理器工厂)对象
  2. 通过实体类管理器工厂获取实体管理器
  3. 获取事务对象,开启事务
  4. 完成增删改查操作
  5. 提交事务(回滚事务)
  6. 释放资源
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。

总结增、删、改、查方法:

  • 查:
    1. find(Class class,T key),使用立即加载,参数为查询实体类的字节码、主键对应的实体类字段;
    2. getReference(Class class,T key),使用延迟加载,参数为查询实体类的字节码、主键对应的实体类字段。调用该方法不会立即发送sql语句查询数据库,什么时候用,什么时候查询数据库。
  • 删:remove(Object o),传入要删除的对象实体类,该对象实体类主键必须要有值。
  • 改:merge(Object o),传入要更新的对象,对象主键字段需要赋值。
  • 增:persist(Object o),传入一个要增加的对象,如果设置了主键自增策略,此处可以不用传入主键值。

1.5、jpql查询

面向对象的一种查询方式,里面封装了一些高级查询方法。查询的是表和表中的字段。具体方法和操作步骤如下:

  1. 查询所有
@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();
}
  1. 排序查询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();
}
  1. 聚合函数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();
    }
  1. 分页查询
@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();
}
  1. 模糊查询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、快速入门

  1. 创建工程、导入依赖
<?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>
  1. 配置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>

  1. 编写实体类,使用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 + '\'' +
                '}';
    }
}

  1. 编写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> {
}
  • 只要直接继承两个接口就有操作数据库的基本方法
  1. 测试
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、原理剖析

  1. 通过JdkDynamicAopProxy的invoke方法创建了一个动态代理对象
  2. 代理对象SimpleJpaRepository当中封装了JPA的操作
  3. 通过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、使用

  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 +
                '}';
    }
}

  1. 创建对应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> {
}

  1. 测试
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、删除

  1. 默认(一的一方对外键进行了维护)

如果要删除一的一方,会将多的一方外键置为空

  1. 放弃维护(一方放弃维护外键)

这时将不能删除一方,会出错,因为外键被占用,而一方又因为不能维护所以不能修改。

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<>();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值