chivehao
chivehao
发布于 2022-07-23 / 56 阅读
0
0

Spring Data JPA

JPA

一、概念介绍

JPA 的全称是 Java Persistence API, 即 Java 持久化 API,是 SUN 公司推出的一套基于 ORM 的规范,内部是由一系列的接口和抽象类构成。JPA 通过 JDK 5.0 注解描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。

二、开发包介绍

由于 JPA 是 sun 公司制定的 API 规范,所以我们不需要导入额外的 JPA 相关的 jar 包,只需要导入 JPA 的提供商的 jar 包。我们选择 Hibernate 作为 JPA 的提供商,所以需要导入 Hibernate 的相关 jar 包。下载网址在官网

三、玩转 JPA 小 demo

需求:保存番剧到数据库

1.搭建开发环境

这里使用的是 IDEA 社区版,创建了个 maven 项目

2.导入 maven 依赖(坐标)

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.hibernate.version>5.0.7.Final</project.hibernate.version>
    </properties>

    <dependencies>
        <!-- junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- hibernate对jpa的支持包 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${project.hibernate.version}</version>
        </dependency>

        <!-- c3p0 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-c3p0</artifactId>
            <version>${project.hibernate.version}</version>
        </dependency>

        <!-- log日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <!-- Mysql and MariaDB -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
    </dependencies>

3.实体类和数据库表的映射配置

package com.tobeshrek.entity;

import javax.persistence.*;

/**
 * 番剧实体类
 *      配置映射关系
 *          1.实体类和表的映射关系
 *          2.实体类中属性和表中字段的映射关系
 */
@Entity//声名实体类
@Table(name = "anime")//建立实体类和表之间的关系
public class Anime {
    /**
     * 主键生成策略
     *      GenerationType.IDENTITY :自增,底层数据库必须支持自动增长(底层数据库支持的自动增长方式,对id自增,如MySQL。
     *      GenerationType.SEQUENCE : 序列,底层数据库必须支持序列,如Oracle。
     *      GenerationType.TABLE : jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
     *      GenerationType.AUTO : 由程序自动的帮助我们选择主键生成策略
     */
    @Id//声名主键的配置
    @GeneratedValue(strategy = GenerationType.IDENTITY)//声名主键生成策略为自动增长
    @Column(name = "anime_id")//声名配置 实体类属性 和 表中字段 的映射关系,name为数据库中字段的名称,不可省略
    private Long animeId;//主键ID

    @Column(name = "title")
    private String title;//番剧标题

    @Column(name = "intro")
    private String intro;//番剧简介


    // set 和 get 方法
    public Long getAnimeId() {
        return animeId;
    }

    public void setAnimeId(Long animeId) {
        this.animeId = animeId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getIntro() {
        return intro;
    }

    public void setIntro(String intro) {
        this.intro = intro;
    }
}

4.配置 JAP 的核心配置文件

在 Java 工程的 resource 路径下创建一个名为 META-INF 的文件夹,在此文件夹下创建一个名为 persistence.xml 的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
    http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
    <!--需要配置persistence-unit节点
        持久化单元:
            name:持久化单元名称
            transaction-type:事务管理的方式
                    JTA:分布式事务管理
                    RESOURCE_LOCAL:本地事务管理
    -->
    <persistence-unit name="animePersistenceUnitJPA" transaction-type="RESOURCE_LOCAL">
        <!--JPA的实现方式,这里选用hibernate实现-->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <!--可选配置:配置jpa实现方的配置信息-->
        <properties>
            <!-- 数据库信息
                用户名,javax.persistence.jdbc.user
                密码,  javax.persistence.jdbc.password
                驱动,  javax.persistence.jdbc.driver
                数据库地址   javax.persistence.jdbc.url
            -->
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="mima"/>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/>
            <!--配置jpa实现方(hibernate)的配置信息
                自动创建数据库表    :  hibernate.hbm2ddl.auto
                       create      : 程序运行时创建数据库表(如果有表,先删除表再创建)
                       update      :程序运行时创建表(如果有表,不会创建表)
                       none        :不会创建表
                显示sql           :   false|true
                格式化显示的sql           :   false|true
                数据库方言
           -->
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.show.sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
        </properties>
    </persistence-unit>
</persistence>

5.编写测试类

流程:除了第四步不相同,其它基本相同,下面只提取第四部重要的部分

        //1.加载配置文件创建工厂(实体管理器工厂)对象
        entityManagerFactory = Persistence.createEntityManagerFactory("animePersistenceUnitJPA");
        //2.通过实体管理器工厂获取实体管理器
        entityManager = entityManagerFactory.createEntityManager();
        //3.获取事务对象,开启事务
        transaction = entityManager.getTransaction();
        transaction.begin();
        //4.完成增删改查操作
        Anime anime = new Anime();
        anime.setTitle("紫罗兰永恒花园");
        anime.setIntro("一位少女追寻爱的故事");
        entityManager.persist(anime);
        //5.提交事务(回滚事务)
        transaction.commit();
        //6.释放资源
        if(entityManager.isOpen()) entityManager.close();
        //if(entityManagerFactory.isOpen()) entityManagerFactory.close();
//根据id查询番剧,在调用find方法的时候,就会发送sql语句查询数据库
Anime anime = entityManager.find(Anime.class, 1L);

//根据id查询番剧,延迟加载(懒加载),得到的是一个动态代理对象,什么时候用,什么使用才会查询
Anime anime = entityManager.getReference(Anime.class, 1L);

//测试删除番剧,先查询后删除
Anime anime = entityManager.getReference(Anime.class, 1L);
entityManager.remove(anime);

//测试更新,先查询后更新
Anime anime = entityManager.getReference(Anime.class, 1L);
anime.setTitle("幸运星");
anime.setIntro("女の子の日常");
entityManager.merge(anime);
    /**
     * 添加番剧
     */
    @Test
    public void add(){
        //4.完成增删改查操作
        Anime anime = new Anime();
        anime.setTitle("紫罗兰永恒花园");
        anime.setIntro("一位少女追寻爱的故事");
        Anime anime1 = new Anime();
        anime1.setTitle("幸运星");
        anime1.setIntro("女の子の日常らしい");
        Anime anime2 = new Anime();
        anime2.setTitle("利兹与青鸟");
        anime2.setIntro("讲述友情");
        Anime anime3 = new Anime();
        anime3.setTitle("女子高中生");
        anime3.setIntro("女子高中生");
        entityManager.persist(anime);
        entityManager.persist(anime1);
        entityManager.persist(anime2);
        entityManager.persist(anime3);
    }

    /**
     * 查询全部
     *      jpql:from com.tobeshrek.Anime
     *      sql:SELECT * FROM anime
     */
    @Test
    public void testFindAll() {
        //定义jpql语句
        String jpql = "from Anime";
        //创建查询对象
        Query query = entityManager.createQuery(jpql);
        //查询结果
        List<Anime> list = query.getResultList();
        System.out.println("查询结果总数"+list.size());
        //打印结果
        for (Anime anime : list) {
            System.out.println(anime);
        }
    }
     /**
     * 排序查询: 倒序查询全部番剧(根据id倒序)
     *      sql:SELECT * FROM anime ORDER BY anime_id DESC
     *      jpql:from Anime order by animeId desc
     *
     * 进行jpql查询
     *      1.创建query查询对象
     *      2.对参数进行赋值
     *      3.查询,并得到返回结果
     */
    @Test
    public void testFindAllOrder() {
        //定义jpql语句
        String jpql = "from Anime order by animeId desc";
        //创建查询对象
        Query query = entityManager.createQuery(jpql);
        //查询结果
        List<Anime> list = query.getResultList();
        System.out.println("查询结果总数"+list.size());
        //打印结果
        for (Anime anime : list) {
            System.out.println(anime);
        }
    }

    /**
     * 使用jpql查询,统计客户的总数
     *      sql:SELECT COUNT(anime_id) FROM anime
     *      jpql:select count(animeId) from Anime
     */
    @Test
    public void testCount(){
        //定义jpql语句
        String jpql = "select count(animeId) from Anime";
        //创建查询对象
        Query query = entityManager.createQuery(jpql);
        //发送查询
        Object o = query.getSingleResult();
        //打印结果
        System.out.println("查询总数"+o);
    }
    /**
     * 分页查询
     *      sql(MySql):select * from anime limit 0,2
     *      jpql:from Anime
     */
    @Test
    public void testPaged(){
        //创建查询对象
        Query query = entityManager.createQuery("from Anime");
        //设置分页条件
        query.setFirstResult(0);    //从第一(0+1)页开始
        query.setMaxResults(2);     //每页显示2条
        //发送语句
        List animeList = query.getResultList();
        //打印结果
        for (Object o : animeList) {
            System.out.println(o);
        }
    }
    /**
     * 条件查询
     *     案例:查询番剧简介以‘女’开头("女%")的番剧
     *          sql:SELECT * FROM anime WHERE intro LIKE  ?
     *          jpql : from Anime where intro like ?
     */
    @Test
    public void testCondition(){
        //创建查询对象
        Query query = entityManager.createQuery("from Anime where intro like ?");
        //设置查询条件,占位符的索引位置(从1开始)
        query.setParameter(1,"女%");
        //发送语句
        List animeList = query.getResultList();
        //打印结果
        for (Object o : animeList) {
            System.out.println(o);
        }
    }

Spring Data JPA

概述

Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套 JPA 应用框架,可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!

与 hibernate 之间的关系

JPA 是一套规范,内部是有接口和抽象类组成的。hibernate 是一套成熟的 ORM 框架,而且 Hibernate 实现了 JPA 规范,所以也可以称 hibernate 为 JPA 的一种实现方式,我们使用 JPA 的 API 编程,意味着站在更高的角度上看待问题(面向接口编程)
Spring Data JPA 是 Spring 提供的一套对 JPA 操作更加高级的封装,是在 JPA 规范下的专门用来进行数据持久化的解决方案。

搭建开发环境,常规数据库CRUD

1.导入 maven 坐标

    <properties>
        <spring.version>4.2.4.RELEASE</spring.version>
        <hibernate.version>5.0.7.Final</hibernate.version>
        <slf4j.version>1.6.6</slf4j.version>
        <log4j.version>1.2.12</log4j.version>
        <c3p0.version>0.9.1.2</c3p0.version>
        <mysql.version>5.1.6</mysql.version>
    </properties>

    <dependencies>
        <!-- junit单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.9</version>
            <scope>test</scope>
        </dependency>

        <!-- spring beg -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.8</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- spring end -->

        <!-- hibernate beg -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.2.1.Final</version>
        </dependency>
        <!-- hibernate end -->

        <!-- c3p0 beg -->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>${c3p0.version}</version>
        </dependency>
        <!-- c3p0 end -->

        <!-- log end -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <!-- log end -->


        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.9.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>

        <!-- el beg 使用spring data jpa 必须引入 -->
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>2.2.4</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>javax.el</artifactId>
            <version>2.2.4</version>
        </dependency>
        <!-- el end -->
    </dependencies>

2.整合 Spring Data JPA 与 Spring

在 resource 文件夹下创建 applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/data/jpa
        http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

    <!--spring 和 spring data jpa的配置-->

    <!--1.创建数据库连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="root"></property>
        <property name="password" value="mima"></property>
        <property name="jdbcUrl" value="jdbc:mysql:///jpa" ></property>
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    </bean>

    <!-- 2.创建entityManagerFactory对象交给spring容器管理-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!--配置的扫描的包(实体类所在的包) -->
        <property name="packagesToScan" value="com.tobeshrek.entity" />
        <!-- jpa的实现厂家 -->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
        </property>

        <!--jpa的供应商适配器 -->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!--配置是否自动创建数据库表 -->
                <property name="generateDdl" value="false" />
                <!--指定数据库类型 -->
                <property name="database" value="MYSQL" />
                <!--数据库方言:支持的特有语法 -->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
                <!--是否显示sql -->
                <property name="showSql" value="true" />
                <property name="format_sql" value="true" />
                <!--自动创建数据库表-->
                <property name="hbm2ddl.auto" value="update" />
            </bean>
        </property>

        <!--jpa的方言 :高级的特性 -->
        <property name="jpaDialect" >
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
        </property>
    </bean>



    <!--3.整合spring dataJpa-->
    <jpa:repositories base-package="com.tobeshrek.dao" transaction-manager-ref="transactionManager"
                      entity-manager-factory-ref="entityManagerFactory" ></jpa:repositories>

    <!--4.配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"></property>
    </bean>

    <!-- 4.txAdvice-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!-- 5.aop-->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.tobeshrek.service.*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
    </aop:config>

    <!--5.声明式事务 -->
    <!-- 6. 配置包扫描-->
    <context:component-scan base-package="com.tobeshrek" ></context:component-scan>
</beans>

3.使用 JPA 注解配置映射关系

已配置

4,编写符合 Spring Data JPA 规范的 Dao 层接口

/**
 * JpaRepository<实体类类型,主键类型>:用来完成基本CRUD操作
 * JpaSpecificationExecutor<实体类类型>:用于复杂查询(分页等查询操作)
 */
public interface AnimeDao extends JpaRepository<Anime,Long>, JpaSpecificationExecutor<Anime> {
}

5.完成基本 CRUD 操作

@RunWith(SpringJUnit4ClassRunner.class) //声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//指定spring容器的配置信息
public class AnimeDaoTest {

    @Autowired
    private AnimeDao animeDao;

    /**
     * 添加番剧
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void add(){
        Anime anime = new Anime();
        anime.setTitle("玉子市场");
        anime.setIntro("玉子爱情故事");
        animeDao.save(anime);
    }
    /**
     * 查询番剧
     *      findOne  对应 find方法
     *      getOne   对应 getReference方法
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void getOne(){
        Anime anime = animeDao.getOne(4L);
        System.out.println(anime);
    }
    /**
     * 更新,查询修改再保存
     */
    @Test
    @Transactional
    @Rollback(false)
    public void update(){
        Anime anime = animeDao.getOne(3L);
        anime.setTitle("CLANNAD");
        anime.setIntro("这是一个讲述亲情和爱情的故事");
        animeDao.save(anime);
    }
    /**
     * 删除
     */
    @Test
    @Transactional
    @Rollback(false)
    public void delete(){
        animeDao.delete(4L);
    }
}

6.spring data jpa 实现原理

简单来说就是灵活运用了动态代理,所有的 Dao 接口用一种代理对象实现

7.JPQL 查询

使用Spring Data JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用@Query注解,结合JPQL的语句方式完成查询
@Query 注解的使用非常简单,只需在方法上面标注该注解,同时提供一个JPQL查询语句即可

    //@Query 使用jpql的方式查询。
    @Query(value="from Anime")
    public List<Anime> findAllJPQL();

    //@Query 使用jpql的方式查询。?1代表参数的占位符,其中1对应方法中的参数索引
    @Query(value="from Anime where title = ?1")
    public Anime findOneJPQL(String titles);

    //测试代码
    @Test
    @Transactional
    @Rollback(false)
    public void testFindJPQL(){
        List<Anime> list = animeDao.findAllJPQL();
        for (Anime anime : list) {
            System.out.println(anime);
        }
        Anime anime = animeDao.findOneJPQL("幸运星");
        System.out.println(anime);
    }

也可以通过使用 @Query 来执行一个更新操作,为此,我们需要在使用 @Query 的同时,用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询

    //@Query 使用jpql的方式更新
    @Query("update Anime set intro=?2 where title=?1")
    @Modifying//方法参数一般和查询语句占位符顺序相同,不相同如上指定位置
    public void updateIntroByTitle(String title, String intro);

    //测试代码
    @Test
    @Transactional
    @Rollback(false)
    public void testUpateJPQL(){
        Anime anime = animeDao.getOne(3L);
        System.out.println(anime);//Anime{animeId=3, title='CLANNAD', intro='这是一个讲述亲情和爱情的故事'}
        animeDao.updateIntroByTitle("CLANNAD","触动人心的亲情和令人羡慕的爱情故事");
    }

8.使用SQL语句查询

Spring Data JPA同样也支持sql语句的查询

    //使用原生SQL查询
    @Query(value="select * from anime",nativeQuery=true)//nativeQuery : 使用本地sql的方式查询
    public List<Anime> findALLBySql();

    //测试代码
    @Test
    @Transactional
    @Rollback(false)
    public void testSQL(){
        List<Anime> list = animeDao.findALLBySql();
        for (Anime anime : list) {
            System.out.println(anime);
        }
    }

9.方法命名规则查询

根据方法的名字,就能创建查询。只需要按照Spring Data JPA提供的方法命名规则定义方法的名称,就可以完成查询工作。Spring Data JPA在程序执行的时候会根据方法名称进行解析,并自动生成查询语句进行查询
按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。
具体的关键字,使用方法和生产成SQL如下表所示:
notfount

    //方法命名方式查询(根据番剧标题查询番剧)实体类属性第一个字母大写
    public Anime findByTitle(String title);

    //测试代码
   Anime anime = animeDao.findByTitle("CLANNAD");
   System.out.println(anime);

复杂CRUD操作

1.Specifications 动态查询

有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象

//JpaSpecificationExecutor中定义的方法
//根据条件查询一个对象
T findOne(Specification<T> spec); 
//根据条件查询集合
List<T> findAll(Specification<T> spec);
//根据条件分页查询
Page<T> findAll(Specification<T> spec, Pageable pageable);
//排序查询查询
List<T> findAll(Specification<T> spec, Sort sort);
//统计查询
long count(Specification<T> spec);

对于JpaSpecificationExecutor,这个接口基本是围绕着Specification接口来定义的。我们可以简单的理解为,Specification构造的就是查询条件。
Specification接口中只定义了如下一个方法

/**
* root :Root接口,代表查询的根对象,可以通过root获取实体中的属性
* query :代表一个顶层查询对象,用来自定义查询
* cb :用来构建查询,此对象里有很多条件方法
**/
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);

2.使用Specifications完成条件查询

    @Test
    @Transactional
    @Rollback(false)
    public void testSpecifications(){
        //使用匿名内部类的方式,创建一个Specification的实现类,并实现toPredicate方法
        Specification<Anime> spec = new Specification<Anime>() {
            public Predicate toPredicate(Root<Anime> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                //root:从实体Customer对象中按照custName属性进行查询
                Path<Object> title = root.get("title");//注意这里是实体类属性
                //cb:构建查询,添加查询方式   like:模糊匹配
                //Predicate predicate = cb.equal(title, "CLANNAD");//根据标题精准匹配
                Predicate predicate = cb.like(title.as(String.class),"C%");//根据标题模糊查询
                return predicate;
            }
        };
        //发送语句
        //Anime anime = animeDao.findOne(spec);//根据标题精准匹配
        //System.out.println(anime);
        List<Anime> animeList = animeDao.findAll(spec);//根据标题模糊查询
        for (Anime anime : animeList) {
            System.out.println(anime);
        }
    }

3.使用Specifications的分页查询

    /**
     * 使用Specifications的分页查询
     */
    @Test
    public void testPage(){
        /**
         * 构造分页参数
         *         Pageable : 接口
         *             PageRequest实现了Pageable接口,调用构造方法的形式构造
         *                 第一个参数:页码(从0开始)
         *                 第二个参数:每页查询条数
         */
        Pageable pageable = new PageRequest(0,2);
        //发送语句
        Page<Anime> page = animeDao.findAll(pageable);
        System.out.println("总记录数:"+page.getTotalElements());//总记录数:3
        System.out.println("总页数:"+page.getTotalPages());     //总页数:2
        for (Anime anime : page.getContent()) {                 //Anime{animeId=1, title='紫罗兰永恒花园', intro='一位少女追寻爱的故事'}
            System.out.println(anime);                          //Anime{animeId=2, title='幸运星', intro='女の子の日常らしい'}
        }
    }

对于Spring Data JPA中的分页查询,是其内部自动实现的封装过程,返回的是一个Spring Data JPA提供的pageBean对象。其中的方法说明如下

//获取总页数
int getTotalPages();
//获取总记录数  
longgetTotalElements();
//获取列表数据
List<T> getContent();

4.方法对应关系

多表之间的关系和操作多表的操作步骤

1.表关系

一对一
就是特殊的一对多

一对多
一的一方:主表
多的一方:从表
外键:需要再从表上新建一列作为外键,他的取值来源于主表的主键

多对多
中间表:中间表中最少应该由两个字段组成,这两个字段做为外键指向两张表的主键,又组成了联合主键

2.实体类中的关系

包含关系:可以通过实体类中的包含关系描述表关系
继承关系

3.分析步骤

1.明确表关系
2.确定表关系(描述 外键|中间表)
3.编写实体类,再实体类中描述表关系(包含关系)
4.配置映射关系

4.完成多表操作

新建类,配置好属性和数据库映射
用户表user

/**
 * 用户实体类
 */
@Entity
@Table(name = "user")
public class User {
    /**
     * 主键ID,自增
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    /**
     * 用户名
     */
    @Column(name = "username")
    private String username;
    /**
     * 用户密码
     */
    @Column(name = "password")
    private String password;
}

一对多操作

一的一方均不维护主键

    /**
     * 使用注解的形式配置多表关系
     *      1.声明关系
     *          @OneToMany : 配置一对多关系
     *              targetEntity :对方对象的字节码对象
     *      2.配置外键(中间表)
     *              @JoinColumn : 配置外键
     *                  name:外键字段名称
     *                  referencedColumnName:参照的主表的主键字段名称
     * 放弃外键维护权
     *      mappedBy:对方配置关系的属性名称\
     * cascade : 配置级联(可以配置到设置多表的映射关系的注解上)
     *      CascadeType.all         : 所有
     *                  MERGE       :更新
     *                  PERSIST     :保存
     *                  REMOVE      :删除
     * fetch : 配置关联对象的加载方式
     *          EAGER   :立即加载
     *          LAZY    :延迟加载
     */

配置用户与番剧的一对多

//User.java
    //配置用户和番剧的一对多关系
    @OneToMany(mappedBy = "user",cascade = CascadeType.MERGE)
    private Set<Anime> Animes = new HashSet<Anime>();
//Anime.java
    //多对一关系映射:多个番剧对应用户
    @ManyToOne(targetEntity = User.class,fetch = FetchType.LAZY,cascade = CascadeType.ALL)
    @JoinColumn(name = "user_anime_id",referencedColumnName = "user_id")//这里是对方数据库字段名称
    private User user;//用它的主键,对应联系人表中的外键

创建DAO层,代码不贴了

测试代码

        //新建二次猿对象
        User user = new User();
        user.setUsername("平泽唯");
        user.setPassword("123");
        //新建番剧对象
        Anime anime = new Anime();
        anime.setTitle("轻音少女");
        anime.setIntro("喝茶部日常");
        //设置关系
        anime.setUser(user);
        //保存
        animeDao.save(anime);

多对多操作

这里用User和Goods的多对多
创建Goods实体类并配置映射关系

/**
 * 周边实体类
 */
@Entity
@Table(name = "goods")
public class Goods {
    /**
     * 主键ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "goods_id")
    private Long goodsId;

    /**
     * 商品标题
     */
    @Column(name = "title")
    private String title;

    /**
     * 商品价格
     */
    @Column(name = "price")
    private Float price;
}

配置多对多关系

    /**
     * 配置用户到角色的多对多关系
     *      配置多对多的映射关系
     *          1.声明表关系的配置
     *              @ManyToMany(targetEntity = Role.class)  //多对多
     *                  targetEntity:代表对方的实体类字节码
     *          2.配置中间表(包含两个外键)
     *                @JoinTable
     *                  name : 中间表的名称
     *                  joinColumns:配置当前对象在中间表的外键
     *                      @JoinColumn的数组
     *                          name:外键名
     *                          referencedColumnName:参照的主表的主键名
     *                  inverseJoinColumns:配置对方对象在中间表的外键
     */
//User.java
    //配置和周边表多对多关系映射
    @ManyToMany(mappedBy="users",cascade = CascadeType.ALL)
    private Set<Goods> goodsSet = new HashSet<Goods>();
//Goods.java
    //多对多关系映射
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name="user_goods",//中间表的名称
            //中间表user_goods字段关联goods表的主键字段goods_id
            joinColumns={@JoinColumn(name="goods_id",referencedColumnName="goods_id")},
            //中间表user_goods的字段关联user表的主键user_id
            inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")})
    private Set<User> users = new HashSet<User>();
//测试代码
        //新建二次猿对象
        User user = new User();
        user.setUsername("秋山mio");
        user.setPassword("123");
        //新建周边对象
        Goods goods = new Goods();
        goods.setTitle("康娜手办");
        //设置关系
        goods.getUsers().add(user);
        //保存
        goodsDao.save(goods);

对象导航查询

对象图导航检索方式是根据已经加载的对象,导航到他的关联对象。它利用类与类之间的关系来检索对象。


评论