02. JPA的使用

JPA的使用

一、maven工程搭建

基于IDEA开发工具


jpa项目结构.png

1). maven坐标 pom.xml

<packaging>jar</packaging>

<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>

2). resources/META-INF/persistence.xml

jpa规定的文件目录和文件名!

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <!--
    需要配置persistence-unit节点
    持久化单元:
      name:持久化单元名称
      transaction-type:事务管理的方式
         JTA:分布式事务管理
         RESOURCE_LOCAL:本地事务管理
    -->
    <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
        <!--jpa的实现方式 -->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

        <!--可选配置:配置jpa实现方的配置信息-->
        <properties>
            <!--
            数据库信息
            用户名,javax.persistence.jdbc.user
            密码,  javax.persistence.jdbc.password
            驱动,  javax.persistence.jdbc.driver
            数据库地址   javax.persistence.jdbc.url
            -->
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="abc123"/>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/>

            <!--
            配置jpa实现方(hibernate)的配置信息
            hibernate.show_sql是否在控制台显示sql :
              false
              true
            hibernate.hbm2ddl.auto自动创建数据库表
              create : 程序运行时创建数据库表(了解,如果有表,先删除表再创建)
              update : 程序运行时创建表(了解,如果有表,不会创建表)
              none   : 不会创建表【实际开发中使用】
            -->
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.hbm2ddl.auto" value="none" />
        </properties>
    </persistence-unit>
</persistence>

3). 数据库准备

CREATE DATABASE IF NOT EXISTS jpa DEFAULT CHARACTER SET utf8;
USE jpa;

/*创建客户表*/
CREATE TABLE cst_customer (
    cust_id BIGINT(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
    cust_name VARCHAR(32) NOT NULL COMMENT '客户名称(公司名称)',
    cust_source VARCHAR(32) DEFAULT NULL COMMENT '客户信息来源',
    cust_industry VARCHAR(32) DEFAULT NULL COMMENT '客户所属行业',
    cust_level VARCHAR(32) DEFAULT NULL COMMENT '客户级别',
    cust_address VARCHAR(128) DEFAULT NULL COMMENT '客户联系地址',
    cust_phone VARCHAR(64) DEFAULT NULL COMMENT '客户联系电话',
    PRIMARY KEY (`cust_id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

二、实体类关系映射配置详解 Customer

所有的注解都是使用JPA的规范提供的注解,在导入注解包的时候,一定要导入javax.persistence下的

package com.lingting.pojo;

import javax.persistence.*;

/**
 *
 * 实体类,配置映射关系
 *   1.实体类和表的映射关系注解
 *      @Entity: 声明当前类是实体类
 *      @Table : 配置实体类和表的映射关系
 *          name : 配置数据库表的名称
 *   2.实体类中属性和表中字段的映射关系
 */
@Entity
@Table(name = "cst_customer")
public class Customer {

/** @GeneratedValue:配置主键的生成策略,strategy 值如下:
*
* GenerationType.IDENTITY :自增,mysql
*   底层数据库必须支持自动增长(底层数据库支持的自动增长方式,对id自增):注解用法
*      @Id
*      @GeneratedValue(strategy = GenerationType.IDENTITY)
*      @Column(name = "cust_id")
*      ...
*
* GenerationType.SEQUENCE : 序列,oracle
*   底层数据库必须支持序列:用法
*      @Id
*      @GeneratedValue(strategy = GenerationType.SEQUENCE,generator="payablemoney_seq")
*      @SequenceGenerator(name="payablemoney_seq", sequenceName="seq_payment")
*      ...
*
*      SequenceGenerator注解字段源码含义
*         String name();
*            表示该表主键生成策略的名称,它被引用在@GeneratedValue中设置的“generator”值中
*         String sequenceName() default "";
*            属性表示生成策略用到的数据库序列名称。
*         int initialValue() default 0;
*            表示主键初识值,默认为0
*         int allocationSize() default 50;
*            表示每次主键值增加的大小,例如设置1,则表示每次插入新记录后自动加1,默认为50
*
* GenerationType.AUTO :
*   由程序自动的帮助我们选择主键生成策略: 用法
*      @Id
*      @GeneratedValue(strategy = GenerationType.AUTO)
*      ...
*
* GenerationType.TABLE :
*   jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增,用法
*      @Id
*      @GeneratedValue(strategy = GenerationType.TABLE, generator="pk_gen")
*      @TableGenerator(name = "pk_gen",
*          table="tb_generator",    // 表名
*          pkColumnName="gen_name", // 主键生成策略所对应键值 字段名
*          pkColumnValue="PAYABLEMOENY_PK", // 主键的值
*          valueColumnName="gen_value", // 主键生成的值,会累加
*          allocationSize=1 // 主键生成的值累加的步长
*      )
*      ...
*
*      TableGenerator注解字段源码含义
*          String name();
*             表示该表主键生成策略的名称,它被引用在@GeneratedValue中设置的“generator”值中
*          String table() default "";
*             表示表生成策略所持久化的表名,例如,这里表使用的是数据库中的“tb_generator”。
*          String catalog() default "";
*             String schema() default "";
*          catalog和schema具体指定表所在的目录名或是数据库名
*          String pkColumnName() default "";
*              属性的值表示在持久化表中,该主键生成策略所对应键值的名称。
*              例如在“tb_generator”中将“gen_name”作为主键的键值
*          String pkColumnValue() default "";
*              属性的值表示在持久化表中,该生成策略所对应的主键。
*              例如在“tb_generator”表中,将“gen_name”的值为“CUSTOMER_PK”。
*          String valueColumnName() default "";
*              属性的值表示在持久化表中,该主键当前所生成的值,它的值将会随着每次创建累加。
*              例如,在“tb_generator”中将“gen_value”作为主键的值
*          int initialValue() default 0;
*              表示主键初识值,默认为0。
*          int allocationSize() default 50;
*              表示每次主键值增加的大小,例如设置成1,则表示每次创建新记录后自动加1,默认为50。
*          UniqueConstraint[] uniqueConstraints() default {};
*/
    @Id // 声明主键的配置
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cust_id") // 配置属性和字段的映射关系
    private Long custId; // 客户主键

    /**
     * @Column:配置属性和字段的映射关系
     *    name:数据库表中字段的名称
     *    unique:是否唯一
     *    nullable:是否可以为空
     *    inserttable:是否可以插入
     *    updateable:是否可以更新
     *    columnDefinition: 定义建表时创建此列的DDL
     *    secondaryTable: 从表名。如果此列不建在主表上(默认建在主表),
     *         该属性定义该列所在从表的名字搭建开发环境
     */
    @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;//客户地址

    /* get/set/toString方法请自动生成! */
}

三、抽离jpa工厂创建 JpaUtils

实体管理器工厂 是一个线程安全的类!通过静态代码块的形式,工具类在初始化时,创建一个公共的实体管理器工厂对象

package com.lingting.utils;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;


public class JpaUtils {

    private static EntityManagerFactory factory;

    static {
        // 加载配置文件, 创建工厂对象,指定【持久化单元】配置,见META-INF/persistence.xml文件
        factory = Persistence.createEntityManagerFactory("myJpa");
    }

    /**
     * 获取EntityManger对象: 完成持久化操作的核心对象
     * 让实体类与底层数据源进行O/R映射管理:方法说明
     *    getTransaction : 获取事务对象
     *    persist : 保存操作
     *    merge : 更新操作
     *    remove : 删除操作
     *    find : 根据id查询:立即查询
     *    getReference : 根据id查询:延迟查询,
     *        返回实体类pojo的一个代理对象;
     *        没有真正查询;只是增强了各个get方法;(切面)
     *        在执行get方法之前调用【一次数】据库查询!
     *        之后的调用则不会去查询!
     *   获取事务对象EntityTransaction:
     *   EntityManger对象.getTransaction():方法概述
     *     begin:开启事务
     *     commit:提交事务
     *     rollback:回滚事务
     */
    public static EntityManager getEntityManager() {
        return factory.createEntityManager();
    }

    /**
     * 归还资源
     */
    public static void close(EntityManager entityManager) {
        entityManager.close();
    }

    /**
     * 彻底关闭资源
     * @param entityManager
     */
    public static void closeFactory(EntityManager entityManager) {
        close(entityManager);
        factory.close();
    }
}

四、jpa基本增删改查

1. JpaTest/JpqlTest测试类模板

package com.lingting.test;

import com.lingting.pojo.Customer;
import com.lingting.utils.JpaUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

/**
 * 测试jpa 一般步骤
 * 1. 加载配置文件, 创建 实体管理器工厂对象(线程安全,静态)【交给工具类完成】
 * 2. 通过工厂获取实体类管理器
 * 3. 获取事务,开启事务
 * 4. 增删改查
 * 5. 提交事务/回滚事务
 * 6. 释放资源
 */
public class JpaTest { // 或者为JpqlTest类

    private EntityManager em;

    private EntityTransaction tx;

    @Before
    public void init() {
        em = JpaUtils.getEntityManager();
        tx = em.getTransaction();
    }

    /** 以下代码写在此处 */
}

2). 增

/**
 * 增
 */
@Test
public void persist() {
    // 开启事务
    tx.begin();

    // 创建实体类,进行保存
    Customer customer = new Customer();
    customer.setCustName("星环城");
    customer.setCustIndustry("太空能源开发");

    // 保存
    em.persist(customer);

    // 提交事务
    tx.commit();

    // 释放资源
    em.close();
}

3). 改

/**
 * 改: 根据id修改,null值会覆盖原有值
 */
@Test
public void merge() {
    tx.begin();
    Customer customer = em.find(Customer.class, 1L);
    customer.setCustName("新星环城");
    customer.setCustIndustry("太空能源开发");
    // 清空缓存,把customer对象从缓存中清除
    em.clear();
    em.merge(customer);
    tx.commit();
    em.close();
}

4). 删

/**
 * 删除:根据id进行删除
 */
@Test
public void remove() {
    tx.begin();
    try {
        Customer customer = em.find(Customer.class, 1L);

        /* ~~~~ 清空id,清空缓存,再删除会失败 ~~~~
        customer.setCustId(null);
        em.clear();
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

        em.remove(customer);
        tx.commit();
    } catch (Exception e) {
        tx.rollback();
    } finally {
        em.close();
    }
}

5). 查

/**
 * 立即查询:根据id查询客户
 * 查询不需要事务吧!
 */
@Test
public void find() {
    // 第一个参数为pojo字节码对象,第二个为id
    Customer customer = em.find(Customer.class, 1L);
    System.out.println(customer);
    em.close();
}
/**
 * 懒加载(延迟加载):动态代理
 * 返回pojo的动态代理类,在get等方法中配置查询数据库的切面,
 * 只有第一次调用时回去查询!
 */
@Test
public void getReference() {
    Customer reference = em.getReference(Customer.class, 2L);
    System.out.println(reference);
    em.close();
}
/**
 * EntityManager 的实体缓存现象!
 */
@Test
public void cache() {
    Customer c1 = em.find(Customer.class, 2L);
    Customer c2 = em.find(Customer.class, 2L);
    System.out.println(c1 == c2);
    // 清空缓存,对象就不一样了!
    em.clear();
    Customer c3 = em.find(Customer.class, 3L);
    System.out.println(c2 == c3);
}

五、jpql复杂查询 JpqlTest

test模板如上 四、...

1). 查询全部

/**
 * 查询全部: jpql 不支持 `select *`
 * sql:SELECT * FROM cst_customer
 * jqpl:from cn.itcast.domain.Customer
 * 或者简写包名
 */
@Test
public void findAll() {
    String jpql = "from Customer";

    // 创建Query查询对象,query对象才是指定jpql的对象
    Query query = em.createQuery(jpql);

    // 发送查询,并封装结果集
    List list = query.getResultList();
    list.forEach(System.out::println);

    em.close();
}

2). 分页 + 倒叙

/**
 * 分页查询 + 倒叙查询【先倒叙,后分页;如果要反过来,需要进行复杂查询,jpa的方式很麻烦】
 * sql:select * from cst_customer ORDER BY cust_id DESC limit ?,?
 * jqpl : from Customer order by custId desc
 */
@Test
public void pageQuery() {

    String jpql = "from Customer order by custId desc";
    Query query = em.createQuery(jpql);

    // 偏移量 = (查询页码 - 1) * 每页条数
    query.setFirstResult((2 - 1) * 2);

    // 每页查询的条数
    query.setMaxResults(2);

    /**
     * getResultList : 直接将查询结果封装为list集合
     * getSingleResult : 得到唯一的结果集
     */
    List list = query.getResultList();
    list.forEach(System.out::println);
    em.close();
}

3). 条件查询

/**
 * 条件查询:以 “新” 开头的客户
 * sql:SELECT * FROM cst_customer WHERE cust_name LIKE  ?
 * jpql : from Customer where custName like ?
 */
@Test
public void conditionQuery() {
    String jpql = "from Customer where custName like ?";
    Query query = em.createQuery(jpql);

    // 设置参数
    query.setParameter(1, "新%");
    List list = query.getResultList();
    list.forEach(System.out::println);
    em.close();
}

3). 聚合查询

/**
 * 聚合查询,统计总数
 * sql:SELECT COUNT(cust_id) FROM cst_customer
 * jpql:select count(custId) from Customer
 */
@Test
public void aggregationQuery() {
    String jpql = "select count(custId) from Customer";
    Query query = em.createQuery(jpql);
    Object singleResult = query.getSingleResult();
    System.out.println(singleResult);
    em.close();
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容