文档章节

JPA的常用操作和配置总结

architect刘源源
 architect刘源源
发布于 2018/01/15 13:05
字数 2574
阅读 22
收藏 1

基本操作

    基本的操作无非就是增删查改这么几个。我们结合一个示例来看看每个具体的操作过程。首先我们定义两个对象实体,分别为PersonInformation和Address。假设PersonInformation和Address是一对多的关系,他们的具体定义实现如下:

 

Java代码  收藏代码

  1. package model;  
  2.   
  3. import java.util.List;  
  4.   
  5. import javax.persistence.CascadeType;  
  6. import javax.persistence.Column;  
  7. import javax.persistence.Entity;  
  8. import javax.persistence.EnumType;  
  9. import javax.persistence.Enumerated;  
  10. import javax.persistence.FetchType;  
  11. import javax.persistence.GeneratedValue;  
  12. import javax.persistence.GenerationType;  
  13. import javax.persistence.Id;  
  14. import javax.persistence.JoinColumn;  
  15. import javax.persistence.Lob;  
  16. import javax.persistence.OneToMany;  
  17. import javax.persistence.Table;  
  18.   
  19. @Entity  
  20. @Table(name="person_information")  
  21. public class PersonInformation {  
  22.     @Id  
  23.     @Column(name = "person_id", unique = true, nullable = false)  
  24.     @GeneratedValue(strategy = GenerationType.IDENTITY)  
  25.     private long id;  
  26.       
  27.     @Column(name = "name", nullable = false)  
  28.     private String name;  
  29.       
  30.     @Column(name = "age", nullable = false)  
  31.     private int age;  
  32.       
  33.     @Column(name = "marriage_status")  
  34.     @Enumerated(EnumType.STRING)  
  35.     private MarriageStatus marriageStatus;  
  36.       
  37.     @Lob  
  38.     @Column(name = "self_description", length = 512)  
  39.     private String selfDescription;  
  40.       
  41.     @OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)  
  42.     @JoinColumn(name = "person_id")  
  43.     private List<Address> addresses;  
  44. }  

    Address的实现如下:

 

 

Java代码  收藏代码

  1. package model;  
  2.   
  3. import javax.persistence.Column;  
  4. import javax.persistence.Entity;  
  5. import javax.persistence.GeneratedValue;  
  6. import javax.persistence.GenerationType;  
  7. import javax.persistence.Id;  
  8. import javax.persistence.Table;  
  9.   
  10. @Entity  
  11. @Table(name="address")  
  12. public class Address {  
  13.   
  14.     @Id  
  15.     @Column(name = "id", unique = true, nullable = false)  
  16.     @GeneratedValue(strategy = GenerationType.IDENTITY)  
  17.     private int id;  
  18.       
  19.     @Column(name = "country")  
  20.     private String country;  
  21.       
  22.     @Column(name = "province")  
  23.     private String province;  
  24.       
  25.     @Column(name = "city")  
  26.     private String city;  
  27.       
  28.     @Column(name = "street")  
  29.     private String street;  
  30.       
  31.     @Column(name = "building")  
  32.     private String building;  
  33.       
  34.     @Column(name = "room")  
  35.     private String room;  
  36. }  

    这些代码里忽略了对元素的get, set访问操作方法。详细的实现可以看后面附件里的代码。上述的代码里还专门针对enum类型,长的text类型的映射定义。上面定义生成的表结构如下图:

 

 

    在这个定义的基础上,我们先来看增加一个元素的代码如何实现。

    假定我们要保存一个完整的PersonInformation对象到数据库里,它有一个对应的Address列表,可以对应一个或多个Address对象。我们希望最理想的情况就是定义好PersonInformation对象和对应的Address对象之后,能够自动将两者都映射到数据库中。

     下面是实现增加一个对应到数据库里的代码片段:

 

Java代码  收藏代码

  1. PersonInformationService service = new PersonInformationService(em);  
  2.         PersonInformation person = new PersonInformation();  
  3.         person.setAge(20);  
  4.         person.setMarriageStatus(MarriageStatus.single);  
  5.         person.setName("frank");  
  6.         person.setSelfDescription("Funny");  
  7.           
  8.         Address address = new Address();  
  9.         address.setCountry("China");  
  10.         address.setProvince("Beijing");  
  11.         address.setCity("Beijing");  
  12.         address.setStreet("ShangDi");  
  13.         address.setBuilding("Ring");  
  14.         address.setRoom("302");  
  15.           
  16.         List<Address> list = new ArrayList<Address>();  
  17.         list.add(address);  
  18.         person.setAddresses(list);  
  19.           
  20.         service.createPersonInformation(person);  

    我们将实现添加元素的功能封装在PersonInformationService类里,该类里createPersonInformation方法的实现如下:

 

 

Java代码  收藏代码

  1. public void createPersonInformation(PersonInformation person) {  
  2.         if(person == null)  
  3.             throw new IllegalArgumentException("Invalid entity argument.");  
  4.         EntityTransaction transaction = em.getTransaction();  
  5.         transaction.begin();  
  6.         em.persist(person);  
  7.         transaction.commit();  
  8.     }  

    我们通过启动一个transaction,然后将更新提交到数据库保证更新的完整性。

 

    对应删除元素的操作有几种,基本的是根据一个指定的primary key来删除对应的元素,一个典型的实现如下:

Java代码  收藏代码

  1. public void removePersonInformation(long id) {  
  2.         PersonInformation person =  
  3.                 getSinglePersonInformationById(id);  
  4.         EntityTransaction transaction = em.getTransaction();  
  5.         transaction.begin();  
  6.         em.remove(person);  
  7.         transaction.commit();  
  8.     }  

    如果我们仔细考虑这个示例的话,会发现一个比较有意思的地方。我们指定的是删除PersonInformation对象,实际上它是关联了Address对象的,如果我们查数据库会发现PersonInformation对象被删除的同时Address对象也被删除了。这是因为在前面的定义里我们有如下部分:

Java代码  收藏代码

  1. @OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)  
  2. @JoinColumn(name = "person_id")  
  3. private List<Address> addresses;  

   我们这里的CascadeType.ALL指定了级联的读取和删除操作。

 

    JPA对查询的操作支持也有好几种方式,一种最简单的就是根据primary key查找对应的元素,它的典型实现如下:

 

Java代码  收藏代码

  1. public PersonInformation getSinglePersonInformationById(long id) {  
  2.     return em.find(PersonInformation.class, id);  
  3. }  

    还有一种查询的方式是通过类似于SQL的脚本方式,我们称之为JQL。比如说我们想要查找年龄大于24岁而且已婚的person信息,一种实现如下:

Java代码  收藏代码

  1. public List<PersonInformation> getMarriedPersons() {  
  2.     List<PersonInformation> result = em.createQuery(  
  3.             "select p from PersonInformation p where p.age > 24 "  
  4.             + "and p.marriageStatus = model.MarriageStatus.married").getResultList();  
  5.     return result;  
  6. }  

 

    我们修改很多的数据需要更新会考虑到update一些数据,并将他们反应到数据库中,它的实现则比较简单。比如说我们需要修改marriageStatus状态信息,则实现如下:

Java代码  收藏代码

  1. public void updateMarriageStatus(long id, MarriageStatus status) {  
  2.     EntityTransaction transaction = em.getTransaction();  
  3.     transaction.begin();  
  4.     PersonInformation person =  
  5.             getSinglePersonInformationById(id);  
  6.     person.setMarriageStatus(status);  
  7.     em.merge(person);  
  8.     transaction.commit();  
  9. }  

    很多其他状态数据的修改我们都可以采用类似的方式。

    综合看前面几种数据访问的代码,我们还发现一个比较有意思的地方。就是凡是牵涉到修改数据的地方,比如增加数据,删除数据或者修改数据的时候,我们倾向于使用transaction。采用事物的方式可以保证我们数据更新的原子性和一致性。

 

常用配置

    除了前面的几种基本操作,有一些配置对于我们来说也很重要,比如我们希望能看到jpa生成的sql代码,以方便后续进行性能调优。有的时候我们也会想,以前用jdbc写代码的时候,为了提升性能,会考虑使用连接池,那么jpa里有没有支持呢?这几点我们都一一道来。

自动生成表

    我们知道jpa的一个重要的好处就是它可以自动生成我们定义的表格。我们将定义的对象实体和数据库的表做好了映射,所以它能够做到这一点。这样有一个好处就是减少了我们和纯SQL语句打交道的机会。在我们前面的一些示例里我们可能已经看到了,我们在配置文件里只需要指定连接的数据库名字和用户名密码,然后其他都不需要考虑。如果我们运行程序的时候,甚至都没有建表,可以程序也可以很好的运行,而且数据库里表也就很神奇的给建立起来了。其实这个功能的实现主要在于如下的配置:

Xml代码  收藏代码

  1. <property name="eclipselink.ddl-generation" value="create-tables" />  
  2. <property name="eclipselink.ddl-generation.output-mode" value="database" />  

    我们可以尝试将原来的数据库删除了,然后只是建一个库而不建对应的表。然后运行程序之后再去看结果。

 

生成SQL语句

    在eclipselink里,生成sql语句其实比较简单。就好比我们输出日志一样,通过查看jpa生成的sql我们可以看到它运行的一些机制。

    要启用这个特性,我们只需要在配置文件persistence.xml文件里加入如下部分就可以了:

Xml代码  收藏代码

  1. <property name="eclipselink.logging.level.sql" value="FINE"/>  
  2. <property name="eclipselink.logging.parameters" value="true"/>  

    这两个property项放在properties属性内。

 

线程池

    以前分析过线程池的一些简单实现。当时用jdbc访问数据库的时候,考虑到每个具体的连接都由连接池管理的话可以充分利用数据库的资源,也能够提升系统的性能。在最初实现一个简单的jpa示例之后我就想到,如果jpa也能支持线程池的话那就完美了。其实它的配置也比较简单:

Xml代码  收藏代码

  1. <property name="eclipselink.connection-pool.default.initial" value="1" />  
  2. <property name="eclipselink.connection-pool.default.min" value="64" />  
  3. <property name="eclipselink.connection-pool.default.max" value="64" />  

    这个典型的配置里,我们默认的线程池初始连接数量为1,在一些情况下当请求增加的时候,它的最大值可以增加到64。这里也可以将最小的线程数量设置为64。这些我们都可以根据需要来进行调整。

 

cache相关

    和任何一个系统访问操作相关的地方,我们都很关注性能。cache就是一个非常重要的提升手段。现在想起来,它用的好可以很好的节省时间,用的不好也会带来很多不必要的麻烦。之所以有这部分的感想主要是在于以前经历的一个项目中碰到过一个设计不合理的问题。

    在系统里,如果我们希望通过JPA作为系统的数据访问层,那么它将作为系统访问数据库统一的接口。对于所有通过它来访问数据库的操作,它的支持都很好。在我们系统的一个案例里,却存在一种绕过JPA通过另外一种方式访问数据库的情况。这样在两个进程访问同一个数据库时,如果一个进程修改了数据库的内容,另外一个进程假定是使用JPA访问的话,它一般是不知道的。这样就可能有这么一个情况,假设有数据A,它原来的内容是"abc",但是另外一个进程将它修改成"def"后原来采用JPA访问的进程读到的还是老的"abc"。这是什么原因呢?就因为JPA里默认是支持cache的。所以对于通过JPA来访问的进程来说它们没有修改任何数据,JPA也不会认为它的cache失效了。为了解决这个问题,只能禁用JPA的cache,保证每次访问都去数据库里取最新的数据。这种配置结果如下:

Xml代码  收藏代码

  1. <property name="eclipselink.cache.shared.default" value="false"/>  
  2. <property name="eclipselink.cache.size.default" value="0"/>  
  3. <property name="eclipselink.cache.type.default" value="None"/>  
  4. <property name="eclipselink.query-results-cache" value="false"/>  

    这里主要禁用的cache有共享的部分以及将查询的结果cache禁用。

    总的来说,禁用cache对于系统的性能来说有非常大的影响。在这个案例里作出这样的选择其根源在于没有采用统一的数据访问方式。这种不合理的方式也让我们更深刻的理解为什么系统要定义一个统一的数据访问层而不是随意多个。

 

总结

    JPA它本身是java orm实现的一个规范,除了我们常听到的hibernate,还有eclipselink, openjpa等实现。因为有了同样的一个规范,他们之间可以实现很好的兼容。对于数据库来说这些最常见的增删查改等操作是最基础的。除了这些以外,我们还需要考虑到系统的性能,一些情况下的跟踪调试等等。这些就牵涉到线程池、cache和输出sql语句的跟踪等方面。这里举的例子都是采用eclipselink,采用其他的实现也都有类似的功能。

© 著作权归作者所有

architect刘源源

architect刘源源

粉丝 176
博文 564
码字总数 941209
作品 0
浦东
程序员
私信 提问
Servlet 3.0 新特性

Servlet 3.0 作为 Java EE 6 规范体系中一员,随着 Java EE 6 规范一起发布。该版本在前一版本(Servlet 2.5)的基础上提供了若干新特性用于简化 Web 应用的开发和部署。其中有几项特性的引入...

壹炮倾城
2013/06/13
174
1
Servlet 3.0 新特性详解

简介: Servlet 是 Java EE 规范体系的重要组成部分,也是 Java 开发人员必须具备的基础技能,Servlet 3.0 是 Servlet 规范的最新版本。本文主要介绍了 Servlet 3.0 引入的若干重要新特性,包...

红薯
2010/04/23
1K
3
技术专题讨论:如何对 JPA 或者 MyBatis 进行技术选型

在我们平时的项目中,大家都知道可以使用 JPA 或者 Mybatis 作为 ORM 层。对 JPA 和 Mybatis 如何进行技术选型? 下面看看大精华总结如下: 最佳回答 首先表达个人观点,JPA必然是首选的。 ...

后海
2018/06/27
1K
0
Java Web-Servlet

章节目录 什么是Servlet Servlet 、ServletContext、Servlet Container、web 容器之间的区别 Servlet、ServletConfig、GenericServlet、HttpServlet、自定义Servlet 之间的联系 HttpServlet ......

markfork
2018/06/14
0
0
Servlet 3.0 特性详解

Servlet 是 Java EE 规范体系的重要组成部分,也是 Java 开发人员必须具备的基础技能,本文主要介绍了 Servlet 3.0 引入的若干重要新特性,包括异步处理、新增的注解支持、可插性支持等等,为...

那位先生
2015/06/12
266
0

没有更多内容

加载失败,请刷新页面

加载更多

使用TensorFlow的AI程序运行报错AttributeError: module 'tensorflow' has no attribute 'xxx'

使用TensorFlow的AI程序,在运行时报错AttributeError: module 'tensorflow' has no attribute 'xxx',首先检查是否是包路径不对,一般是版本变化所致。...

织梦之魂
28分钟前
2
0
提示浏览器版本低

本文转载于:专业的前端网站➭提示浏览器版本低 网站网页在遇到浏览器低版本(尤其是IE浏览器)时,提示浏览器版本低(如IE8以及以下),建议用户升级浏览器以获得最好体验。以下是代码: 1...

前端老手
30分钟前
4
0
CentOS 7系统增加swap

转载请注明文章出处:CentOS 7系统增加swap swap是位于磁盘上的特殊文件(或分区),属于“虚拟内存”的一部分。通俗点就是内存的备胎,内存充足的情况下,基本上没swap什么事(和设置有关)...

tlanyan
53分钟前
6
0
基于Prometheus和Grafana的监控平台 - 环境搭建

相关概念 微服务中的监控分根据作用领域分为三大类,Logging,Tracing,Metrics。 Logging - 用于记录离散的事件。例如,应用程序的调试信息或错误信息。它是我们诊断问题的依据。比如我们说...

JAVA日知录
今天
6
0
PHP运行时全局构造体

struct _php_core_globals { zend_bool magic_quotes_gpc; // 是否对输入的GET/POST/Cookie数据使用自动字符串转义。 zend_bool magic_quotes_runtime; //是否对运行时从外部资源产生的数据使...

冻结not
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部