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();
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容