Spring Data Jpa 介绍和详细入门案例搭建

介绍

简介:

Spring Data Jpa是 dao层的一个框架,简化数据库开发。作用和Mybatis框架一样,但是使用方式和底层机制有所不同。最大的差别就是,使用Spring Data Jpa开发,很多场景不需要写SQL。
官方网站:https://spring.io/projects/spring-data-jpa

什么是Spring Data jpa

Spring Data JPA 是 Spring 基于JPA 规范的基础上封装的⼀套 JPA 应⽤框架,可使开发者⽤极简的
代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常⽤功能!
学习并使⽤Spring Data JPA 可以极⼤提⾼开发效率。
Spring Data JPA 极⼤简化了数据访问层代码。
如何简化呢?使⽤了Spring Data JPA,我们Dao层中只需要写接⼝,不需要写实现类,就⾃动具有
了增删改查、分⻚查询等⽅法。

Spring Data家族

Spring Data Jpa 介绍和详细入门案例搭建_第1张图片

Spring Data Jpa ,JPA规范,和Hibernate

发展历史

  1. 最初对数据库的操作直接使用JDBC来操作
  2. 后来,出现了ORM思想,对象关系映射,通过操作java的pojo对象,来操作数据库数据
  3. 基于ORM思想,出现了一堆ORM的框架 例如Hibernate(全自动)、toplink等。Hibernate是最典型的ORM框架,相比之下,Mybatis拥抱SQL和ORM框架更靠近面向对象,不建议写sql,所以mybatis是sql映射框架,而不是ORM框架。mybatis算半自动的orm框架,因此也更灵活。Hibernate这种ORM框架完全可以通过对象关系模型,时间对数据库的操作。
  4. 不同的ORM框架,对ORM思想有不同的实现,SUN公司对此提出了统一的规范,这个规范就是JPA规范 java持久化API(java persistence api)。hibernate框架是满足JPA规范的。JPA 是⼀套规范,内部是由接⼝和抽象类组成的,Hiberanate 是⼀套成熟的 ORM 框架,⽽且Hiberanate 实现了 JPA 规范,所以可以称 Hiberanate 为 JPA 的⼀种实现⽅式,我们使⽤ JPA 的 API 编程,意味着站在更⾼的⻆度去看待问题(⾯向接⼝编程)
  5. Spring Data JPA 正是对JPA规范做了一层封装。Spring Data JPA 是 Spring 提供的⼀套对 JPA 操作更加⾼级的封装,是在 JPA 规范下的专⻔⽤来进⾏数据持久化的解决⽅案。

Spring Data Jpa ,JPA规范,和Hibernate 关系示意图

Spring Data Jpa 介绍和详细入门案例搭建_第2张图片



Spring Data JPA入门

使用Spring Data jpa搭建入门案例,完成对mysql中数据表的简单增删改查、排序、分页等基础操作。

创建项目


开发环境

maven + spring + mysql+hibernate + spring data jpa


创建数据库表

数据库表结构
Spring Data Jpa 介绍和详细入门案例搭建_第3张图片

数据库建表sql

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for tb_resume
-- ----------------------------
DROP TABLE IF EXISTS `tb_resume`;
CREATE TABLE `tb_resume` (
  ìd` bigint(20) NOT NULL AUTO_INCREMENT,
  àddress` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `phone` varchar(255) DEFAULT NULL,
  PRIMARY KEY (ìd`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of tb_resume
-- ----------------------------
BEGIN;
INSERT INTO `tb_resume` VALUES (1, '北京', '张三', '131000000');
INSERT INTO `tb_resume` VALUES (2, '上海', '李四', '151000000');
INSERT INTO `tb_resume` VALUES (3, '⼴州', '王五', '153000000');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

创建maven项目 pom文件如下


<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0modelVersion>

  <groupId>com.lagou.edugroupId>
  <artifactId>spring-data-jpaartifactId>
  <version>1.0-SNAPSHOTversion>



  <dependencies>
    
    <dependency>
      <groupId>junitgroupId>
      <artifactId>junitartifactId>
      <version>4.12version>
      <scope>testscope>
    dependency>


    
    <dependency>
      <groupId>org.springframework.datagroupId>
      <artifactId>spring-data-jpaartifactId>
      <version>2.1.8.RELEASEversion>
    dependency>

    <dependency>
      <groupId>javax.elgroupId>
      <artifactId>javax.el-apiartifactId>
      <version>3.0.1-b04version>
    dependency>
    <dependency>
      <groupId>org.glassfish.webgroupId>
      <artifactId>javax.elartifactId>
      <version>2.2.6version>
    dependency>
    

    
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-aopartifactId>
      <version>5.1.12.RELEASEversion>
    dependency>
    <dependency>
      <groupId>org.aspectjgroupId>
      <artifactId>aspectjweaverartifactId>
      <version>1.8.13version>
    dependency>
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-contextartifactId>
      <version>5.1.12.RELEASEversion>
    dependency>
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-context-supportartifactId>
      <version>5.1.12.RELEASEversion>
    dependency>

    
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-ormartifactId>
      <version>5.1.12.RELEASEversion>
    dependency>
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-beansartifactId>
      <version>5.1.12.RELEASEversion>
    dependency>
    <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-coreartifactId>
      <version>5.1.12.RELEASEversion>
    dependency>
    



    
    <dependency>
      <groupId>org.hibernategroupId>
      <artifactId>hibernate-coreartifactId>
      <version>5.4.0.Finalversion>
    dependency>
    
    <dependency>
      <groupId>org.hibernategroupId>
      <artifactId>hibernate-entitymanagerartifactId>
      <version>5.4.0.Finalversion>
    dependency>
      <dependency>
      <groupId>org.hibernategroupId>
      <artifactId>hibernate-validatorartifactId>
      <version>5.4.0.Finalversion>
    dependency>
      

      
      <dependency>
      <groupId>mysqlgroupId>
      <artifactId>mysql-connector-javaartifactId>
      <version>5.1.46version>
    dependency>
      
      <dependency>
      <groupId>com.alibabagroupId>
      <artifactId>druidartifactId>
      <version>1.1.21version>
    dependency>
      
      <dependency>
      <groupId>org.springframeworkgroupId>
      <artifactId>spring-testartifactId>
      <version>5.1.12.RELEASEversion>
    dependency>
    dependencies>


      
      <build>
      <plugins>
      <plugin>
      <groupId>org.apache.maven.pluginsgroupId>
      <artifactId>maven-compiler-pluginartifactId>
      <version>3.1version>
      <configuration>
      <source>11source>
      <target>11target>
      <encoding>UTF-8encoding>
    configuration>
    plugin>
    plugins>
    build>
    project>

配置Spring项目的配置文件


<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:jpa="http://www.springframework.org/schema/data/jpa"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="
  http://www.springframework.org/schema/beans
  https://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context
  https://www.springframework.org/schema/context/spring-context.xsd
  http://www.springframework.org/schema/data/jpa
  https://www.springframework.org/schema/data/jpa/spring-jpa.xsd
  ">

  

  
  
  <context:property-placeholder location="classpath:jdbc.properties"/>

  
  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
  bean>


  
  <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    

    
    <property name="dataSource" ref="dataSource"/>
    
    <property name="packagesToScan" value="com.lagou.edu.pojo"/>
    
    <property name="persistenceProvider">
      <bean class="org.hibernate.jpa.HibernatePersistenceProvider">bean>
    property>
    
    <property name="jpaDialect">
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect">bean>
    property>

    
    <property name="jpaVendorAdapter" >
      
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        
        
        <property name="generateDdl" value="false"/>

        
        <property name="database" value="MYSQL"/>

        
        <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
        
        <property name="showSql" value="true"/>
      bean>
      
      property>
    bean>
  
        
        <jpa:repositories base-package="com.lagou.edu.dao" entity-manager-factory-ref="entityManagerFactory"
        transaction-manager-ref="transactionManager"/>


        
        <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        	<property name="entityManagerFactory" ref="entityManagerFactory"/>
      	bean>


        
        
        
        <context:component-scan base-package="com.lagou.edu"/>

beans> 

创建java对象和数据库表结构的映射

  • 实体类和数据表映射关系使用注解 @Entity 和 @Table
  • 实体属性和数据库字段映射 @Id 标识主键 @GeneratedValue 标识主键的生成策略 @Column 建立属性和字段映射
  • 这些注解都是属于 javax.persistence.*; 包中
/**
* 简历实体类(在类中要使用注解建立实体类和数据表之间的映射关系以及属性和字段的映射关系)
* 1、实体类和数据表映射关系
* @Entity
* @Table
* 2、实体类属性和表字段的映射关系
* @Id 标识主键
* @GeneratedValue 标识主键的生成策略
* @Column 建立属性和字段映射
*/
@Entity
    @Table(name = "tb_resume")
    public class Resume {

        @Id
        /**
* 生成策略经常使用的两种:
* GenerationType.IDENTITY:依赖数据库中主键自增功能  Mysql
* GenerationType.SEQUENCE:依靠序列来产生主键     Oracle
*/
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id")
        private Long id;
        @Column(name = "name")
        private String name;
        @Column(name = "address")
        private String address;
        @Column(name = "phone")
        private String phone;

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }

        public String getPhone() {
            return phone;
        }

        public void setPhone(String phone) {
            this.phone = phone;
        }


        @Override
        public String toString() {
            return "Resume{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", address='" + address + '\'' +
                ", phone='" + phone + '\'' +
                '}';
        }
    }

编写java对象对应的dao接口 ResumeDao.java

  1. 一个符合SpringDataJpa要求的Dao层接口是需要继承JpaRepository和JpaSpecificationExecutor
  2. JpaRepository<操作的实体类类型,主键类型> 封装了基本的CRUD操作
  3. JpaSpecificationExecutor<操作的实体类类型> 封装了复杂的查询(分页、排序等)
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface ResumeDao extends JpaRepository<Resume,Long>, JpaSpecificationExecutor<Resume> {
	
}

新建单元测试类

import com.lagou.edu.pojo.Resume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
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;
import java.util.Optional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class ResumeDaoTest {
    // 要测试IOC哪个对象注入即可
    @Autowired
    private ResumeDao resumeDao;
    
    
}



简单的增删改查用例

通过主键id查询一条记录

直接使用resumeDao自带的方法就可实现基本的查询,返回Optional对象

@Test
public void testFindById(){
    // 早期的版本 dao.findOne(id);

    /*  方法打印的SQL
        select resume0_.id as id1_0_0_,
        resume0_.address as address2_0_0_, resume0_.name as name3_0_0_,
        resume0_.phone as phone4_0_0_ from tb_resume resume0_ where resume0_.id=?
     */

    Optional<Resume> optional = resumeDao.findById(1l);
    Resume resume = optional.get();
    System.out.println(resume);
}

通过Example来多条件查询

org.springframework.data.domain.Example <目标对象>

@Test
public void testFindOne(){
    Resume resume = new Resume();
    resume.setId(1l);
    resume.setName("张三");
    Example<Resume> example = Example.of(resume);
    Optional<Resume> one = resumeDao.findOne(example);
    Resume resume1 = one.get();
    System.out.println(resume1);
}

通过JpaRepository继承来的save方法,实现新增和更新

新增和更新都使用save方法,通过传入的对象的主键有无来区分,没有主键信息那就是新增,有主键信息就是更新。

@Test
public void testSave(){
    // 新增和更新都使用save方法,通过传入的对象的主键有无来区分,没有主键信息那就是新增,有主键信息就是更新
    Resume resume = new Resume();
    resume.setId(5l);
    resume.setName("赵六六");
    resume.setAddress("成都");
    resume.setPhone("132000000");
    Resume save = resumeDao.save(resume);
    System.out.println(save);

}

根据id删除

@Test
    public void testDelete(){
    resumeDao.deleteById(5l);
}

查询所有数据

@Test
    public void testFindAll(){
    List<Resume> list = resumeDao.findAll();
    for (int i = 0; i < list.size(); i++) {
        Resume resume =  list.get(i);
        System.out.println(resume);
    }
}

对结果进行排序

org.springframework.data.domain.Sort
构建Sort对象后,传入Sort对象进行查询,即可对查询结果进行排序

@Test
    public void testSort(){
    Sort sort = new Sort(Sort.Direction.DESC,"id");
    List<Resume> list = resumeDao.findAll(sort);
    for (int i = 0; i < list.size(); i++) {
        Resume resume =  list.get(i);
        System.out.println(resume);
    }
}

对结果进行分页

  1. 新建一个Pageable对象,设置分页值。参数1为页码,参数2为分页大小
  2. 查询的分页结果,保存在Page对象中。
@Test
public void testPage(){
    /**
    * 第一个参数:当前查询的页数,从0开始
    * 第二个参数:每页查询的数量
    */
    Pageable pageable  = PageRequest.of(0,2);
    //Pageable pageable = new PageRequest(0,2);
    Page<Resume> all = resumeDao.findAll(pageable);
    System.out.println(all);
}



jpql查询、原生sql查询、自定义方法命名查询、动态查询用例

jpql查询

  1. 正如Hibernate 有他自己的hql (Hibernate query language) ,JPA定义了自己的查询语言,名为 java persistence query language。
  2. jpql 将SQL语法和简单查询语义绑定在一起·使用这种语言编写的查询是可移植的,可以被编译成所有主流数据库服务器上的SQL。
  3. jpql 特征与原生SQL语句类似,并且完全面向对象,通过类名和属性访问,而不是表名和表的属性。
  4. jpql包含 select 、update、delete的语法,这里简单举个栗子。要详细使用时再查呗

在ResumeDao.java接口中定义一个方法,使用注解完成jpql的编写

@Query("from Resume  where id=?1 and name=?2")
public List<Resume> findByJpql(Long id,String name);

测试调用

@Test
    public void testJpql(){
    List<Resume> list = resumeDao.findByJpql(1l, "张三");
    for (int i = 0; i < list.size(); i++) {
        Resume resume =  list.get(i);
        System.out.println(resume);
    }
}

原生SQL查询

原生SQL查询,也是在dao接口的方法中使用注解编写sql,需要设置注解 nativeQuery = true,该属性默认为false

@Query(value = "select * from tb_resume  where name like ?1 and address like ?2",nativeQuery = true)
public List<Resume> findBySql(String name,String address);
@Test
public void testSql(){
    List<Resume> list = resumeDao.findBySql("李%", "上海%");
    for (int i = 0; i < list.size(); i++) {
        Resume resume =  list.get(i);
        System.out.println(resume);
    }
}

自定义方法命名查询

  1. 这种查询方法就比较离谱了,按照需要对方法名进行命名,框架就能理解我们的查询需求,而不需要编写任何sql
   /**
     * 方法命名规则查询
     * 按照name模糊查询(like)
     *  方法名以findBy开头
     *          -属性名(首字母大写)
     *                  -查询方式(模糊查询、等价查询),如果不写查询方式,默认等价查询
     */
    public List<Resume> findByNameLikeAndAddress(String name,String address);
@Test
public void testMethodName(){
    List<Resume> list = resumeDao.findByNameLikeAndAddress("李%","上海");
    for (int i = 0; i < list.size(); i++) {
        Resume resume =  list.get(i);
        System.out.println(resume);
    }

}

动态查询

  1. 动态查询可以动态的组合想要查询的条件,使用的时候,需要实现 Specification接口,生成对象,传入查询方法
  2. Specification主要包含了 toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder)方法。需要重写该方法来定义查询的条件。
    1. root参数,用来获取对象的属性值
    2. CriteriaQuery 自定义查询方式,一般用不上。
    3. CriteriaBuilder 查询构造器,构造查询的条件。

单个条件查询用例

    @Test
    public void testSpecfication(){

        /**
         * 动态条件封装
         * 匿名内部类
         *
         * toPredicate:动态组装查询条件
         *
         *      借助于两个参数完成条件拼装,,, select * from tb_resume where name='张三'
         *      Root: 获取需要查询的对象属性
         *      CriteriaBuilder:构建查询条件,内部封装了很多查询条件(模糊查询,精准查询)
         *
         *      需求:根据name(指定为"张三")查询简历
          */

        Specification<Resume> specification = new Specification<Resume>() {
            @Override
            public Predicate toPredicate(Root<Resume> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                // 获取到name属性
                Path<Object> name = root.get("name");

                // 使用CriteriaBuilder针对name属性构建条件(精准查询)
                Predicate predicate = criteriaBuilder.equal(name, "张三");
                return predicate;
            }
        };


        Optional<Resume> optional = resumeDao.findOne(specification);
        Resume resume = optional.get();
        System.out.println(resume);

    }

多个条件组合查询

@Test
    public void testSpecficationMultiCon(){

        /**

         *      需求:根据name(指定为"张三")并且,address 以"北"开头(模糊匹配),查询简历
         */

        Specification<Resume> specification = new Specification<Resume>() {
            @Override
            public Predicate toPredicate(Root<Resume> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                // 获取到name属性
                Path<Object> name = root.get("name");
                Path<Object> address = root.get("address");
                // 条件1:使用CriteriaBuilder针对name属性构建条件(精准查询)
                Predicate predicate1 = criteriaBuilder.equal(name, "张三");
                // 条件2:address 以"北"开头(模糊匹配)
                Predicate predicate2 = criteriaBuilder.like(address.as(String.class), "北%");

                // 组合两个条件
                Predicate and = criteriaBuilder.and(predicate1, predicate2);

                return and;
            }
        };


        Optional<Resume> optional = resumeDao.findOne(specification);
        Resume resume = optional.get();
        System.out.println(resume);
    }

总结

  1. Srping Data JPA实现了对JPA规范的ORM框架的又一层封装,对于使用ORM框架的用户来说,切换为JPA的API来实现业务,后期更换ORM框架的话可以做到最少的修改。
  2. 对于Spring来说,他将SUN公司的JPA规范进行了封装,让使用JPA规范的持久层框架用户的操作变得更简单。官方自己说的 Spring Data JPA 旨在通过将工作量减少到实际需要的数量来显著改进数据访问层的实现。

你可能感兴趣的:(开发框架和中间件,mybatis,java,spring)