持久化API【JPA】完全掌握

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 持久化API【JPA】完全掌握

JPA概述

JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
Sun引入新的JPA ORM规范出于两个原因:其一,简化现有Java EE和Java SE应用开发工作;其二,Sun希望整合ORM技术,实现天下归一。

入门案例

在开始之前,我们先用JPA写一个入门案例。
在Eclipse中创建一个JPA Project:
在这里插入图片描述
JPA version选择2.0即可。
项目创建好后,先导入项目jar包,这里我们用HIbernate作为JPA的实现产品,所以导入Hibernate的jar包、JPA的jar包和MySQL数据库的驱动。然后在src目录下创建持久化类Customer:

package com.wwj.jpa.helloworld;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Table(name = "JPA_CUSTOMERS")
@Entity
public class Customer {
   
   

    private Integer id;
    private String lastName;
    private String email;
    private Integer age;

    @GeneratedValue(strategy = GenerationType.AUTO)
    @Id
    public Integer getId() {
   
   
        return id;
    }

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

    @Column(name = "LAST_NAME")
    public String getLastName() {
   
   
        return lastName;
    }

    public void setLastName(String lastName) {
   
   
        this.lastName = lastName;
    }

    public String getEmail() {
   
   
        return email;
    }

    public void setEmail(String email) {
   
   
        this.email = email;
    }

    public Integer getAge() {
   
   
        return age;
    }

    public void setAge(Integer age) {
   
   
        this.age = age;
    }
}

接着我们打开META-INF目录下的persistence.xml文件,这是JPA的配置文件,对其做如下配置:

<?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="jpa_demo" transaction-type="RESOURCE_LOCAL">

        <!-- 声明JPA的实现产品为Hibernate,如果项目中的实现产品只有一种,则可以不写该配置 -->
        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <!-- 添加持久化类 -->
        <class>com.wwj.jpa.helloworld.Customer</class>

        <properties>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="123456"/>

            <!-- 配置JPA实现产品的基本属性 -->
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

最后来编写测试代码:

package com.wwj.jpa.helloworld;

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

public class Main {
   
   

    public static void main(String[] args) {
   
   

        //1、创建EntityManagerFactory
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("jpa_demo");
        //2、创建EntityManager
        EntityManager manager = factory.createEntityManager();
        //3、开启事务
        EntityTransaction transaction = manager.getTransaction();
        transaction.begin();
        //4、进行持久化操作
        Customer customer = new Customer();
        customer.setAge(20);
        customer.setEmail("zhangsan@163.com");
        customer.setLastName("zhangsan");
        manager.persist(customer);
        //5、提交事务
        transaction.commit();
        //6、释放资源
        manager.close();
        factory.close();
    }
}

有Hibernate基础的话你会发现,JPA的实现和Hibernate如出一辙,先获取工厂实例,然后通过工厂获得EntityManager,接着开启事务,进行持久化操作,然后提交事务,最后释放资源。
我们运行测试代码,然后查询数据表:
在这里插入图片描述
到这里入门案例就编写完成了。

基本注解

上面的案例中用到了一些基本注解,容我一一介绍。

  • @Entity:该注解用于实体类声明语句之前, 指出该Java类为实体类,将该实体类映射到数据表中。如果声明一个Customer类,它将映射到数据库中的customer表上
  • @Table:当实体类与其映射的数据库表名不同名时,需要使用@Table注解说明,该注解与@Entity注解并列使用,置于实体类声明语句之前,可写于单独语句行,也可与声明语句同行。@Table注解的常用属性为name,用于指定映射的数据库表名
  • @Id:该注解用于声明一个实体类的属性映射为数据库的主键列,该属性通常置于属性声明语句之前,可与声明语句同行,也可写在单独行上,@Id还可以置于属性的getter方法之前
  • @GeneratedValue:该注解用于声明主键生成策略,通过strategy属性指定。默认情况下,JPA会自动选择一个最适合底层数据库的主键生成策略:SqlServer对应identity,MySQL对应auto increment。strategy属性提供了几种可选择的策略。
    一 IDENTITY:采用数据库ID自增长的方式来自增主键字段,注:Oracle不支持该方式
    一 AUTO:JPA自动选择合适的策略
    一 SEQUENCE:通过序列产生主键,通过@SequenceGenerator注解指定序列名,注:MySQL不支持该方式
    一 TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植
  • @Basic:该注解表示一个简单的属性到数据库表的字段映射,对于没有任何注解的getXXX()方法,默认注解为@Basic,比如上述案例中,email、age属性都没有添加注解,则其默认注解了@Basic
  • @Column:当实体类的属性与其映射的数据库表的列不同名时,需要使用@Column注解进行声明,该注解通常置于实体类的属性声明语句之前,还可与@Id注解一起使用。@Column注解的常用属性是name,用于设置映射数据库表的列名。此外,该注解还包含其它属性,如:nullable、length、unique等

    @Transient注解

    在某些特定的场景下,例如需要在实体类中添加一个提供实体类信息的工具方法。而在前面我们说到,在实体类中,对于没有添加任何注解的属性,JPA会默认为其添加@Basic注解,所以该工具方法也会被映射到数据库表中,但显然我们并不希望这样,这个时候我们就需要用到@Transient注解。
     @Transient
     public String getMessage() {
         
         
         return "lastname:" + lastName + "age" + age;
     }
    
    该注解表示该属性并非一个需要映射到数据库表的属性,ORM框架将会忽略它。所以需要注意的是,如果一个属性并不需要映射到数据库表中,就必须将其注解为@Transient,如果不添加注解,JPA将会默认添加@Basic注解。

    @Temporal注解

    有时候需要将用户的生日作为属性映射到数据库表中,于是你在Customer类中添加Date类型的birthday属性。但是运行后你会发现,数据表所对应的birthday列的类型为datetime类型。我们知道,datetime类型是包含日期和时间信息的值,而生日仅仅只是日期而并不需要时间,那么怎么对映射列的类型做修改呢?我们需要用到@Temporal注解。
    @Temporal(TemporalType.Date)
    public Date getBirthday() {
   
   
        return birthday;
    }

该注解可以在进行属性映射的时候调整Date类型的精度。

到这里JPA的注解就讲完了,相信屏幕前的你也没有看过瘾吧,没关系,我们继续深入了解一下JPA的API。

Persistence

Persistence类是用于获取EntityManagerFactory实例的,该类包含了一个名为createEntityManagerFactory的静态方法。
createEntityManagerFactory方法有如下两个重载:

  • 带有一个参数的方法,方法参数为persistence.xml中的持久化单元名
    在这里插入图片描述
    该单元名可任意,前面编写的案例就是用了这样一种获取方式得到EntityManagerFactory对象
  • 带有两个参数的方法,第一个参数同样为persistence.xml中的持久化单元名,后一个参数是一个Map类型,用于设置JPA的相关属性,如果通过Map集合设置了属性,JPA将忽略其它地方设置的属性。Map集合中存储的对象属性名必须是JPA实现库提供商的名称空间约定的属性名
    Map<String,Object> properties = new HashMap<String,Object>();
    properties.put("hibernate.show_sql",false);
    EntityManagerFactory factory = Persistence.createEntityManagerFactory("jpa_demo", properties);

通过这样的方式也可以获得EntityManagerFactory 对象,我们运行程序,程序正常运行,并且控制台中并未发现sql语句的输出,说明我们在Map集合中添加的属性配置生效了

EntityManagerFactory

EntityManagerFactory与Hibernate中的SessionFactory是类似的,该接口定义了如下方法:

  • createEntityManager():该方法用于创建实体管理器对象实例
  • EntityManager(Map map):该方法用于创建实体管理器对象实例的重载方法,Map参数用于提供EntityManager的属性
  • isOpen():该方法用于检查EntityManagerFactory是否处于打开状态,事实上,实体管理器工厂创建后一直处于打开状态,除非调用close()方法将其关闭
  • close():该方法用于关闭EntityManagerFactory,EntityManagerFactory 关闭后将释放所有资源,调用isOpen()方法将返回false,其它方法不能调用,否则抛出异常

EntityManager

EntityManager类的方法非常简单,有Hibernate基础的话看一眼就掌握了,我就直接通过代码演示了。
find()方法

    @Test
    public void testFind() {
   
   
        Customer customer = entityManager.find(Customer.class,1);
        System.out.println(customer);
    }

运行结果:

Customer [id=1, lastName=zhangsan, email=zhangsan@163.com, age=20, birthday=2019-10-03]

该方法类似Hibernate中Session对象的get()方法
getReference()方法

    @Test
    public void testGetReference() {
   
   
        Customer customer = entityManager.getReference(Customer.class,1);
        System.out.println(customer);
    }

运行结果:

Customer [id=1, lastName=zhangsan, email=zhangsan@163.com, age=20, birthday=2019-10-03]

虽然这两个方法的效果一样,但是getReference()方法其实获得的是代理对象,只有真正去使用该对象才会去发送sql语句,该方法类似于Hibernate中Session对象的load()方法,所以该方法同样会出现懒加载异常问题, 就是在使用该对象之前把EntityManager关闭,就会抛出懒加载异常。
persist()方法

    @Test
    public void testPersist() {
   
   
        Customer customer = new Customer();
        customer.setAge(20);
        customer.setBirthday(new Date());
        customer.setEmail("lisi@163.com");
        customer.setLastName("lisi");
        entityManager.persist(customer);
    }

运行后查询数据库表:
在这里插入图片描述
该方法类似于Hibernate中Session对象的save()方法,会将对象由瞬时态变为持久态。 但两者也有不同之处,如果为对象设置了id属性,persist()方法将会抛出异常,而save()方法并不会。
remove()方法

    @Test
    public void testRemove() {
   
   

        Customer customer = entityManager.find(Customer.class, 2);
        entityManager.remove(customer);
    }

运行后查询数据表:
在这里插入图片描述
该方法类似于Hibernate中Session对象的delete()方法,但和delete()方法不同的是,remove()方法只能删除持久化对象,所以上面的删除操作需要先通过find()方法获得要删除的对象,然后才能删除。

merge()方法
merge()方法比较复杂,我们来仔细研究一下,先看第一种情况:

    @Test
    public void testMerge() {
   
   
        Customer customer = new Customer();
        customer.setAge(20);
        customer.setBirthday(new Date());
        customer.setEmail("wangwu@163.com");
        customer.setLastName("wangwu");
        Customer cst = entityManager.merge(customer);
        System.out.println(customer);
        System.out.println(cst);
    }

运行结果:

Customer [id=null, lastName=wangwu, email=wangwu@163.com, age=20, birthday=Thu Oct 03 21:29:20 CST 2019]
Customer [id=3, lastName=wangwu, email=wangwu@163.com, age=20, birthday=Thu Oct 03 21:29:20 CST 2019]

在这个程序中,JPA会创建一个新的对象,把临时对象的属性复制到新的对象中,然后对新的对象执行持久化操作,所以新的对象中有id,但之前的临时对象没有id。

再看第二种情况:

    @Test
    public void testMerge2() {
   
   
        Customer customer = new Customer();
        customer.setAge(20);
        customer.setBirthday(new Date());
        customer.setEmail("zhaoliu@163.com");
        customer.setLastName("zhaoliu");
        //设置id属性
        customer.setId(1024);
        Customer cst = entityManager.merge(customer);
        System.out.println(customer);
        System.out.println(cst);
    }

运行结果:

Customer [id=1024, lastName=zhaoliu, email=zhaoliu@163.com, age=20, birthday=Thu Oct 03 21:35:07 CST 2019]
Customer [id=4, lastName=zhaoliu, email=zhaoliu@163.com, age=20, birthday=Thu Oct 03 21:35:07 CST 2019]

在这个程序中,我们创建了一个游离对象,因为该对象有id,但还未与数据库产生关联,然后对其执行持久化操作,这时候需要分三种情况。
第一种情况:

  1. 若在EntityManager缓存中没有该对象,
  2. 若在数据库中也没有对应的记录
  3. JPA会创建一个新的对象,然后把当前游离对象的属性复制到新的对象中
  4. 对新创建的对象执行持久化操作

第二种情况:

  1. 若在EntityManager缓存中没有该对象,
  2. 若在数据库有对应的记录
  3. JPA会查询对应的记录,然后返回该记录对应的对象,最后把游离对象的属性复制到返回的对象中
  4. 对返回的对象执行持久化操作

第三种情况:

  1. 若在EntityManager缓存中有该对象,
  2. JPA会把游离对象的属性复制到缓存对象中
  3. 对缓存对象执行持久化操作

EntityManager还有一些不是特别重要的方法,在这里直接列举,就不做代码演示了:

  • flush():该方法用于同步持久上下文环境,即:将持久上下文环境的所有未保存实体的状态信息保存到数据库中
  • setFlushMode():设置持久上下文环境的Flush模式
  • getFlushMode():获取持久上下文环境的Flush模式,返回FlushModeType类的枚举值
  • refresh():用数据库实体记录的值更新实体对象的状态
  • clear():清除持久上下文环境,断开所有关联的实体,如果这时还有未提交的更新则会被撤销
  • contains():判断一个实例是否属于当前上下文环境管理的实体

EntityTransaction

事务相信大家再熟悉不过了,MySQL、JDBC、Hibernate,一直都在学事务,就直接列举它的一些方法了:

  • begin():用于启动一个事务,此后的多个数据库操作将作为整体被提交或撤销,若这时事务已经启动则会抛出异常
  • commit():用于提交当前事务,即:将事务启动后的所有数据库更新操作持久化到数据库中
  • rollback():用于回滚当前事务,即:撤销事务启动后的所有数据库更新,从而不对数据库产生影响
  • setRollbackOnly():使当前事务只能被回滚
  • getRollbackOnly():查看当前事务是否设置了只能回滚标志
  • isActive():查看当前事务是否是活动的,如果返回true,则不能调用begin()方法,否则将抛出异常;如果返回false,则不能调用commit()、rollback()、setRollbackOnly()、getRollbackOnly()等方法,否则将抛出异常

项目源码

https://github.com/blizzawang/jpa_demo

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
6月前
|
SQL Java API
Java一分钟之-JPA查询:JPQL与Criteria API
【6月更文挑战第14天】本文探讨了Java Persistence API (JPA)中的两种查询方式:JPQL和Criteria API。JPQL是面向对象的SQL,适用于简单查询,而Criteria API则提供类型安全的动态查询构造。文章指出了每种方法的常见问题和避免策略,如混淆实体属性与数据库字段、参数绑定错误、过度复杂化和性能问题。建议开发者根据需求选择适当的方法,并关注查询的可读性、可维护性和性能优化。
72 2
|
2月前
|
SQL Java API
深入探索Java的持久化技术——JPA(Java Persistence API)
【10月更文挑战第10天】深入探索Java的持久化技术——JPA(Java Persistence API)
44 0
|
2月前
|
Java API 数据库
深入探索Java的持久化技术——JPA(Java Persistence API)
【10月更文挑战第10天】深入探索Java的持久化技术——JPA(Java Persistence API)
46 0
|
4月前
|
SQL Java 关系型数据库
理解 Java 持久化 API(JPA)
【8月更文挑战第21天】
55 1
|
7月前
|
SQL Java API
Java一分钟之-JPA:Java持久化API简介
【5月更文挑战第14天】Java Persistence API (JPA) 是Java的ORM规范,用于简化数据库操作。常见问题包括实体映射、事务管理和性能问题。避免错误的关键在于明确主键策略、妥善使用事务、优化查询及理解实体生命周期。示例展示了如何定义实体和使用`EntityManager`保存数据。JPA通过标准化API让开发者更专注于业务逻辑,提升开发效率和代码维护性。
83 0
|
17天前
|
JSON API 数据格式
淘宝 / 天猫官方商品 / 订单订单 API 接口丨商品上传接口对接步骤
要对接淘宝/天猫官方商品或订单API,需先注册淘宝开放平台账号,创建应用获取App Key和App Secret。之后,详细阅读API文档,了解接口功能及权限要求,编写认证、构建请求、发送请求和处理响应的代码。最后,在沙箱环境中测试与调试,确保API调用的正确性和稳定性。
|
29天前
|
供应链 数据挖掘 API
电商API接口介绍——sku接口概述
商品SKU(Stock Keeping Unit)接口是电商API接口中的一种,专门用于获取商品的SKU信息。SKU是库存量单位,用于区分同一商品的不同规格、颜色、尺寸等属性。通过商品SKU接口,开发者可以获取商品的SKU列表、SKU属性、库存数量等详细信息。
|
1月前
|
JSON API 数据格式
店铺所有商品列表接口json数据格式示例(API接口)
当然,以下是一个示例的JSON数据格式,用于表示一个店铺所有商品列表的API接口响应
|
2月前
|
编解码 监控 API
直播源怎么调用api接口
调用直播源的API接口涉及开通服务、添加域名、获取API密钥、调用API接口、生成推流和拉流地址、配置直播源、开始直播、监控管理及停止直播等步骤。不同云服务平台的具体操作略有差异,但整体流程简单易懂。
|
20天前
|
JSON API 数据安全/隐私保护
拍立淘按图搜索API接口返回数据的JSON格式示例
拍立淘按图搜索API接口允许用户通过上传图片来搜索相似的商品,该接口返回的通常是一个JSON格式的响应,其中包含了与上传图片相似的商品信息。以下是一个基于淘宝平台的拍立淘按图搜索API接口返回数据的JSON格式示例,同时提供对其关键字段的解释