热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JPA入门使用、JPA优势、JPA环境搭建

JPA入门使用JPA简介JPA由E

JPA入门使用

JPA简介

  • JPA由EJB 3.0软件专家组开发,作为JSR-220实现的一部分。但它又不限于EJB 3.0,你可以在Web应用、甚至桌面应用中使用。JPA的宗旨是为POJO提供持久化标准规范,由此可见,经过这几年的实践探索,能够脱离容器独立运行,方便开发和测试的理念已经深入人心了。Hibernate3.2+、TopLink 10.1.3以及OpenJPA都提供了JPA的实现。
  • JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。Sun引入新的JPA ORM规范出于两个原因:其一,简化现有Java EE和Java SE应用开发工作;其二,Sun希望整合ORM技术,实现天下归一。
  1. JPA包括以下3方面技术:
    • ORM映射元数据:JPA支持XML和JDK5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;
    • API:用来操作实体对象,执行CRUD操作,框架在后台替代我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来。
    • 查询语言:这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。

JPA的优势

回顾以下DAO代码,以查找所有用户为例,直接使用JDBC查询用户的代码如下:

List users = new ArrayList();
User user = null;
try{
Connection conn = DBUtil.getConnection();
Statement statement = conn.createStatement();
ResultSet resultSet = statement.executeQuery("select * from users");
while(resultSet.next()){
user = new User();
user.setId(resultSet.getInt(1));
user.setUserName(resultSet.getString(2));
user.setPassword(resultSet.getString(3));
user.setTelephone(resultSet.getString(4));
user.setRegisterDate(resultSet.getDate(5));
user.setSex(resultSet.getInt(6));
users.add(user);
}
}catch(Exception e){
//省略异常处理代码
}finally{
DBUtil.close(resultSet,statement,conn);
}

  • 用JDBC查询返回的是ResultSet对象,ResultSet往往不能直接使用,还需要转换成List,并且通过JDBC查询不能直接得到具体的业务独享。这样在整个查询的过程中,就需要做很多重复性的转换工作。
    使用JPA完成持久化操作,只需要编写如下代码:

EntityManager entityManager = entityManagerFactory.createEntityManager();
TypedQuery<Users> query = entityManager.createQuery("from Users ",Users.class);
List<Users> list = query.getResultList();

  • JPA处理数据库查询时,编写的代码简洁。作为查询结果,可以直接获得一个存储着User实例的List集合实例,能够直接使用,避免了繁琐的重复性的数据转换过程。
    1. 标准化 JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。
    2. 容器级特性的支持 JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。
    3. 简单方便 JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易地掌握。JPA基于非侵入式原则设计,因此可以很容易地和其它框架或者容器集成。
    4. 查询能力 JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是Hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
    5. 高级特性 JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。

JPA环境搭建

在IDE工具中新建Maven工程,使用JPA,需做以下准备工作:

  1. 添加依赖

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.3.7.Final</version>
</dependency>

  1. 创建JPA配置文件/META-INF/persistence.xml
    JPA配置文件主要用于配置数据库连接和运行时所需的各种特性。配置文件必须创建在/META-INF目录下,如果没有请自行创建。

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!-- RESOURCE_LOCAL:采用本地事务管理 -->
<persistence-unit name="JPA" transaction-type="RESOURCE_LOCAL">
<!--JPA需要Provider实现,Hibernate是其中之一-->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!--持久化实体类-->
<class>org.hdax.entity.Users</class>
<properties>
<!--数据库方言-->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
<!--数据库连接驱动类-->
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<!--数据库连接url-->
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=utf-8" />
<!--数据库登录用户名-->
<property name="javax.persistence.jdbc.user" value="root" />
<!--数据库登录密码-->
<property name="javax.persistence.jdbc.password" value="root" />
<!--是否显示sql语句-->
<property name="hibernate.show_sql" value="true" />
<!--自动创建数据库表-->
<property name="hibernate.hbm2ddl.auto" value="create" />
</properties>
</persistence-unit>
</persistence>

  1. 创建持久化类

@Entity
@Table(name = "smbms_users")
public class Users implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "userCode",columnDefinition = "varchar(15)")
private String userCode;
@Column(name = "userName",columnDefinition = "varchar(15)")
private String userName;
@Column(name = "userPassword",columnDefinition = "varchar(15) NOT NULL")
private String userPassword;
@Column(name = "gender",columnDefinition = "int(10)")
private Integer gender;
@Column(name = "birthday",columnDefinition = "date")
private Date birthday;
@Column(name = "address",columnDefinition = "varchar(30) DEFAULT '地址不详'")
private String address;
@Column(name = "usrRole",columnDefinition = "int(20)")
private Integer usrRole;
@Column(name = "idPicPath",columnDefinition = "varchar(100)")
private String idPicPath;
public Users(){
}
//省略getter & setter 方法
}

注解名称作用
@Entity将当前实体类标注为持久化类
@Table实体类与数据库表进行关联 当表名和实体类名称相同时,可省略name
@name用于指明数据库表名
@Entity将当前实体类标注为持久化类
@catalog用于指明数据库名称
@Id用于将Java字段标记为数据库表主键列
@GeneratedValue数据库将在插入时自动为id字段生成一个值数据到表
@strategy当使用id字段的自动生成值时
@SequenceGenerator序列创建序列生成器
@name序列程序定义名称
@sequenceName数据库序列名称
@Column属性名称与字段名称映射
@GeneratedValue数据库将在插入时自动为id字段生成一个值数据到表
@name映射的列名
@unique是否唯一
@nullable是否允许为空
@length对于字符型列,length属性指定列的最大字符长度
@insertable是否允许插入
@updateable是否允许更新
@columnDefinition定义建表时创建此列的DDL
@secondaryTable从表名。如果此列不建 在主表上(默认是主表),该属性定义该列所在从表的名字
@Transient该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性

使用JPA完成持久化操作

  • 完成了以上工程之后,就可以通过JPA API操作数据库。
    使用JPA操作数据库包括5个步骤:
  1. 读取并解析配置文件

//此处JPA名称要和配置文件中标签persistence-unit属性name值一致
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("JPA");

  1. 创建实体管理器

EntityManager entityManager = entityManagerFactory.createEntityManager();

  1. 开启事务管理器

EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();

  1. 数据库操作

entityManager.persist(users);

  1. 提交/回滚事务

//提交
transaction.commit();
//回滚
transaction.rollback();

使用JPA实现按主键查询

在进行修改或删除操作时,应先加载对象,然后再执行修改或删除操作。JPA提供了两种方法按照主键加载对象:

find()
EntityManager entityManager = entityManagerFactory.createEntityManager();
Users users = entityManager.find(Users.class,1);
System.out.println("查询完成!");
/*运行结果如下:
Hibernate:
select
users0_.id as id1_0_0_,
users0_.address as address2_0_0_,
users0_.birthday as birthday3_0_0_,
users0_.gender as gender4_0_0_,
users0_.idPicPath as idPicPat5_0_0_,
users0_.userCode as userCode6_0_0_,
users0_.userName as userName7_0_0_,
users0_.userPassword as userPass8_0_0_,
users0_.userRole as userRole9_0_0_
from
smbms_user users0_
where
users0_.id=?
查询完成!
*/

getReference()
EntityManager entityManager = entityManagerFactory.createEntityManager();
Users users = entityManager.getReference(Users.class,1);
System.out.println("查询完成!");
/*运行结果如下:
查询完成!
*/

EntityManager entityManager = entityManagerFactory.createEntityManager();
Users users = entityManager.getReference(Users.class,1);
System.out.println(users);
System.out.println("查询完成!");
/*运行结果如下:
Hibernate:
select
users0_.id as id1_0_0_,
users0_.address as address2_0_0_,
users0_.birthday as birthday3_0_0_,
users0_.gender as gender4_0_0_,
users0_.idPicPath as idPicPat5_0_0_,
users0_.userCode as userCode6_0_0_,
users0_.userName as userName7_0_0_,
users0_.userPassword as userPass8_0_0_,
users0_.userRole as userRole9_0_0_
from
smbms_user users0_
where
users0_.id=?
org.hdax.entity.Users@e041f0c
查询完成!
*/

  • 通过对比我们发现: find()方法查询到的对象结果无论我们是否使用,数据库均会执行查询操作,当查询对象不存在时返回null getReference()方法只有在使用了查询对象时才会连接数据库执行查询,当查询对象不存在时返回EntityNotFoundException异常。

修改数据

  • 数据的修改都是需要事务支持的。通过刚刚的查询数据,现在我们可以对数据进行删除操作:

EntityManager entityManager = entityManagerFactory.createEntityManager();
Users users = entityManager.find(Users.class,1);
System.out.println(users.getUserName());
users.setUserName("超级系统管理员");
/*运行结果如下:
Hibernate:
select
users0_.id as id1_0_0_,
users0_.address as address2_0_0_,
users0_.birthday as birthday3_0_0_,
users0_.gender as gender4_0_0_,
users0_.idPicPath as idPicPat5_0_0_,
users0_.userCode as userCode6_0_0_,
users0_.userName as userName7_0_0_,
users0_.userPassword as userPass8_0_0_,
users0_.userRole as userRole9_0_0_
from
smbms_user users0_
where
users0_.id=?
系统管理员
*/

此时虽然通过set方法修改了用户的用户名,但是通过运行结果我们可以看出,数据库并没有执行响应的更新数据操作,所以现阶段的数据库修改只是在程序内存中完成的。那么如果添加事务之后程序的运行又会发生哪些变化呢?

EntityManager entityManager = entityManagerFactory.createEntityManager();
//获得事务对象
EntityTransaction transaction = entityManager.getTransaction();
//开启事务
transaction.begin();
Users users = entityManager.find(Users.class,1);
System.out.println(users.getUserName());
users.setUserName("超级系统管理员");
//提交事务
transaction.commit();
/*运行结果如下:
Hibernate:
select
users0_.id as id1_0_0_,
users0_.address as address2_0_0_,
users0_.birthday as birthday3_0_0_,
users0_.gender as gender4_0_0_,
users0_.idPicPath as idPicPat5_0_0_,
users0_.userCode as userCode6_0_0_,
users0_.userName as userName7_0_0_,
users0_.userPassword as userPass8_0_0_,
users0_.userRole as userRole9_0_0_
from
smbms_user users0_
where
users0_.id=?
系统管理员
Hibernate:
update
smbms_user
set
address=?,
birthday=?,
gender=?,
idPicPath=?,
userCode=?,
userName=?,
userPassword=?,
userRole=?
where
id=?
*/

不难发现,程序自动执行了更新操作,通过查看数据库表数据,数据库中的用户信息同样发生了相应的修改,所以我们在执行增删改操作的时候应当加入相应的事务控制语句。

删除数据

EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
Users users = entityManager.find(Users.class,1);
entityManager.remove(users);
transaction.commit();
System.out.println("删除数据成功!");
/*运行结果如下:
Hibernate:
select
users0_.id as id1_0_0_,
users0_.address as address2_0_0_,
users0_.birthday as birthday3_0_0_,
users0_.gender as gender4_0_0_,
users0_.idPicPath as idPicPat5_0_0_,
users0_.userCode as userCode6_0_0_,
users0_.userName as userName7_0_0_,
users0_.userPassword as userPass8_0_0_,
users0_.userRole as userRole9_0_0_
from
smbms_user users0_
where
users0_.id=?
Hibernate:
delete
from
smbms_user
where
id=?
删除数据成功!
*/

JPA中Java对象的四种状态

  • 瞬时(transient):通过new创建对象后,对象并没有like持久化,它并未与数据库中的数据有任何的关联。
  • 持久(persistent):使用EntityManager进行find或者persist操作返回的对象即处于持久状态,此时该对象已经处于持久化上下文中,因此任何对于该实体的更新都会同步到数据库中。
  • 脱管(detached):当事务提交后,处于托管状态的对象就转变为了游离状态。此时该对象已经不处于持久化上下文中,因此任何对于该对象的修改都不会同步到数据库中。
  • 删除(removed):当调用EntityManger对实体进行delete后,该实体对象就处于删除状态。其本质也就是一个瞬时状态的对象。

四种状态之间的转换

在这里插入图片描述


推荐阅读
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 基于事件驱动的并发编程及其消息通信机制的同步与异步、阻塞与非阻塞、IO模型的分类
    本文介绍了基于事件驱动的并发编程中的消息通信机制,包括同步和异步的概念及其区别,阻塞和非阻塞的状态,以及IO模型的分类。同步阻塞IO、同步非阻塞IO、异步阻塞IO和异步非阻塞IO等不同的IO模型被详细解释。这些概念和模型对于理解并发编程中的消息通信和IO操作具有重要意义。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 本文介绍了Java的集合及其实现类,包括数据结构、抽象类和具体实现类的关系,详细介绍了List接口及其实现类ArrayList的基本操作和特点。文章通过提供相关参考文档和链接,帮助读者更好地理解和使用Java的集合类。 ... [详细]
  • 集合的遍历方式及其局限性
    本文介绍了Java中集合的遍历方式,重点介绍了for-each语句的用法和优势。同时指出了for-each语句无法引用数组或集合的索引的局限性。通过示例代码展示了for-each语句的使用方法,并提供了改写为for语句版本的方法。 ... [详细]
  • 这篇文章主要介绍了Python拼接字符串的七种方式,包括使用%、format()、join()、f-string等方法。每种方法都有其特点和限制,通过本文的介绍可以帮助读者更好地理解和运用字符串拼接的技巧。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 本文介绍了Java的公式汇总及相关知识,包括定义变量的语法格式、类型转换公式、三元表达式、定义新的实例的格式、引用类型的方法以及数组静态初始化等内容。希望对读者有一定的参考价值。 ... [详细]
author-avatar
shilohqiu_144
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有