简介
在前面的一篇文章里我介绍了JPA的一些基本配置和几个典型的映射关系实现。经过最近一段时间的应用和学习,对于常用的集中操作和配置也有了一定的了解。在这里一并做一个总结。
基本操作
基本的操作无非就是增删查改这么几个。我们结合一个示例来看看每个具体的操作过程。首先我们定义两个对象实体,分别为PersonInformation和Address。假设PersonInformation和Address是一对多的关系,他们的具体定义实现如下:
package model;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name="person_information")
public class PersonInformation {
@Id
@Column(name = "person_id", unique = true, nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "name", nullable = false)
private String name;
@Column(name = "age", nullable = false)
private int age;
@Column(name = "marriage_status")
@Enumerated(EnumType.STRING)
private MarriageStatus marriageStatus;
@Lob
@Column(name = "self_description", length = 512)
private String selfDescription;
@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
@JoinColumn(name = "person_id")
private List<Address> addresses;
}
Address的实现如下:
package model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="address")
public class Address {
@Id
@Column(name = "id", unique = true, nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "country")
private String country;
@Column(name = "province")
private String province;
@Column(name = "city")
private String city;
@Column(name = "street")
private String street;
@Column(name = "building")
private String building;
@Column(name = "room")
private String room;
}
这些代码里忽略了对元素的get, set访问操作方法。详细的实现可以看后面附件里的代码。上述的代码里还专门针对enum类型,长的text类型的映射定义。上面定义生成的表结构如下图:
在这个定义的基础上,我们先来看增加一个元素的代码如何实现。
增
假定我们要保存一个完整的PersonInformation对象到数据库里,它有一个对应的Address列表,可以对应一个或多个Address对象。我们希望最理想的情况就是定义好PersonInformation对象和对应的Address对象之后,能够自动将两者都映射到数据库中。
下面是实现增加一个对应到数据库里的代码片段:
PersonInformationService service = new PersonInformationService(em);
PersonInformation person = new PersonInformation();
person.setAge(20);
person.setMarriageStatus(MarriageStatus.single);
person.setName("frank");
person.setSelfDescription("Funny");
Address address = new Address();
address.setCountry("China");
address.setProvince("Beijing");
address.setCity("Beijing");
address.setStreet("ShangDi");
address.setBuilding("Ring");
address.setRoom("302");
List<Address> list = new ArrayList<Address>();
list.add(address);
person.setAddresses(list);
service.createPersonInformation(person);
我们将实现添加元素的功能封装在PersonInformationService类里,该类里createPersonInformation方法的实现如下:
public void createPersonInformation(PersonInformation person) {
if(person == null)
throw new IllegalArgumentException("Invalid entity argument.");
EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.persist(person);
transaction.commit();
}
我们通过启动一个transaction,然后将更新提交到数据库保证更新的完整性。
删
对应删除元素的操作有几种,基本的是根据一个指定的primary key来删除对应的元素,一个典型的实现如下:
public void removePersonInformation(long id) {
PersonInformation person =
getSinglePersonInformationById(id);
EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.remove(person);
transaction.commit();
}
如果我们仔细考虑这个示例的话,会发现一个比较有意思的地方。我们指定的是删除PersonInformation对象,实际上它是关联了Address对象的,如果我们查数据库会发现PersonInformation对象被删除的同时Address对象也被删除了。这是因为在前面的定义里我们有如下部分:
@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
@JoinColumn(name = "person_id")
private List<Address> addresses;
我们这里的CascadeType.ALL指定了级联的读取和删除操作。
查
JPA对查询的操作支持也有好几种方式,一种最简单的就是根据primary key查找对应的元素,它的典型实现如下:
public PersonInformation getSinglePersonInformationById(long id) {
return em.find(PersonInformation.class, id);
}
还有一种查询的方式是通过类似于SQL的脚本方式,我们称之为JQL。比如说我们想要查找年龄大于24岁而且已婚的person信息,一种实现如下:
public List<PersonInformation> getMarriedPersons() {
List<PersonInformation> result = em.createQuery(
"select p from PersonInformation p where p.age > 24 "
+ "and p.marriageStatus = model.MarriageStatus.married").getResultList();
return result;
}
改
我们修改很多的数据需要更新会考虑到update一些数据,并将他们反应到数据库中,它的实现则比较简单。比如说我们需要修改marriageStatus状态信息,则实现如下:
public void updateMarriageStatus(long id, MarriageStatus status) {
EntityTransaction transaction = em.getTransaction();
transaction.begin();
PersonInformation person =
getSinglePersonInformationById(id);
person.setMarriageStatus(status);
em.merge(person);
transaction.commit();
}
很多其他状态数据的修改我们都可以采用类似的方式。
综合看前面几种数据访问的代码,我们还发现一个比较有意思的地方。就是凡是牵涉到修改数据的地方,比如增加数据,删除数据或者修改数据的时候,我们倾向于使用transaction。采用事物的方式可以保证我们数据更新的原子性和一致性。
常用配置
除了前面的几种基本操作,有一些配置对于我们来说也很重要,比如我们希望能看到jpa生成的sql代码,以方便后续进行性能调优。有的时候我们也会想,以前用jdbc写代码的时候,为了提升性能,会考虑使用连接池,那么jpa里有没有支持呢?这几点我们都一一道来。
自动生成表
我们知道jpa的一个重要的好处就是它可以自动生成我们定义的表格。我们将定义的对象实体和数据库的表做好了映射,所以它能够做到这一点。这样有一个好处就是减少了我们和纯SQL语句打交道的机会。在我们前面的一些示例里我们可能已经看到了,我们在配置文件里只需要指定连接的数据库名字和用户名密码,然后其他都不需要考虑。如果我们运行程序的时候,甚至都没有建表,可以程序也可以很好的运行,而且数据库里表也就很神奇的给建立起来了。其实这个功能的实现主要在于如下的配置:
<property name="eclipselink.ddl-generation" value="create-tables" />
<property name="eclipselink.ddl-generation.output-mode" value="database" />
我们可以尝试将原来的数据库删除了,然后只是建一个库而不建对应的表。然后运行程序之后再去看结果。
生成SQL语句
在eclipselink里,生成sql语句其实比较简单。就好比我们输出日志一样,通过查看jpa生成的sql我们可以看到它运行的一些机制。
要启用这个特性,我们只需要在配置文件persistence.xml文件里加入如下部分就可以了:
<property name="eclipselink.logging.level.sql" value="FINE"/>
<property name="eclipselink.logging.parameters" value="true"/>
这两个property项放在properties属性内。
线程池
以前分析过线程池的一些简单实现。当时用jdbc访问数据库的时候,考虑到每个具体的连接都由连接池管理的话可以充分利用数据库的资源,也能够提升系统的性能。在最初实现一个简单的jpa示例之后我就想到,如果jpa也能支持线程池的话那就完美了。其实它的配置也比较简单:
<property name="eclipselink.connection-pool.default.initial" value="1" />
<property name="eclipselink.connection-pool.default.min" value="64" />
<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,保证每次访问都去数据库里取最新的数据。这种配置结果如下:
<property name="eclipselink.cache.shared.default" value="false"/>
<property name="eclipselink.cache.size.default" value="0"/>
<property name="eclipselink.cache.type.default" value="None"/>
<property name="eclipselink.query-results-cache" value="false"/>
这里主要禁用的cache有共享的部分以及将查询的结果cache禁用。
总的来说,禁用cache对于系统的性能来说有非常大的影响。在这个案例里作出这样的选择其根源在于没有采用统一的数据访问方式。这种不合理的方式也让我们更深刻的理解为什么系统要定义一个统一的数据访问层而不是随意多个。
总结
JPA它本身是java orm实现的一个规范,除了我们常听到的hibernate,还有eclipselink, openjpa等实现。因为有了同样的一个规范,他们之间可以实现很好的兼容。对于数据库来说这些最常见的增删查改等操作是最基础的。除了这些以外,我们还需要考虑到系统的性能,一些情况下的跟踪调试等等。这些就牵涉到线程池、cache和输出sql语句的跟踪等方面。这里举的例子都是采用eclipselink,采用其他的实现也都有类似的功能。
参考材料
http://stackoverflow.com/questions/2809275/disable-caching-in-jpa-eclipselink
http://stackoverflow.com/questions/12926319/how-to-disable-cache-in-eclipselink
相关推荐
在Java Web开发领域中,Java Persistence API(JPA)是一种广泛使用的对象关系映射(ORM)工具,它能够简化数据库操作并提供强大的数据持久化功能。对于初学者来说,搭建一个能够正常运行JPA的环境可能会遇到不少...
JPA 使用注解和 XML 来配置实体类与数据库表之间的映射关系。本文将详细解读 JPA 注解的基本用法,并通过 Hibernate 框架作为实现 JPA 规范的具体例子。 #### 1. @Entity - **用途**:`@Entity` 注解用来标记一个 ...
在现代互联网开发中,Spring Boot、JPA(Java Persistence API)以及Redis已经成为构建高效、可扩展的应用程序的常用技术栈。本项目结合这三个核心组件,旨在创建一个能够利用缓存优化性能的分布式系统。以下将详细...
### OpenJPA-Manual 关键知识点解析 #### 一、OpenJPA介绍 **1.1 关于本文档** ...以上是对“openjpa-manual”文档的关键知识点的总结,希望能帮助读者更好地理解JPA和OpenJPA的相关概念和技术细节。
在实际使用中,开发者可能需要配置数据库连接信息(如URL、用户名、密码),定义JPA实体类(对应数据库表),实现Repository接口进行数据操作,最后通过Maven命令执行构建和运行。 **总结** "springboot+jpa+oracle...
总结来说,本项目展示了如何利用SpringBoot、SpringSecurity和JPA在IntelliJ IDEA环境下构建一个完整的用户角色权限管理系统,通过security.sql和security-jpa这两个关键组件,实现了数据初始化和核心业务逻辑的构建...
6. **批量更新和删除**:当适用时,使用批量更新和删除操作,它们比单个操作更高效。 7. **数据库反规范化**:为了性能考虑,有时可能需要对数据库进行反规范化,尽管这可能会影响数据完整性。 8. **选择正确的...
总结,JPA是Java开发中处理数据库的强大工具,通过面向对象的方式简化了数据库操作,提供了灵活的查询机制,并且具备事务管理和性能优化功能。初学者应掌握其核心概念和常用API,以便更好地进行数据库操作。在实际...
在Java开发中,数据持久化是一个关键环节,而Java Persistence API (JPA) 和 Hibernate 是常用的ORM(对象关系映射)框架,它们简化了数据库操作。这篇文章将深入探讨如何使用JPA与Hibernate实现CRUD(创建、读取、...
SSH框架,全称为Struts2、Spring和Hibernate的集成,是Java Web开发中常用的一套技术栈。本环境基于SSH,并引入了JPA(Java Persistence API)作为数据持久层的解决方案,同时配合MySQL数据库,提供了一个完整的开发...
Struts2、Spring2.5和JPA是Java开发中常用的三大框架,它们在企业级应用开发中扮演着重要角色。Struts2作为MVC框架,负责处理HTTP请求和视图展示;Spring2.5则提供了强大的依赖注入(DI)和面向切面编程(AOP)功能...
JPA 允许开发者以声明的方式定义 Java 对象与关系型数据库之间的映射关系,从而简化了对数据库的操作。本文档主要介绍 JPA 中的批注(Annotation)及其使用方法。 #### 二、JPA 批注的重要性 JPA 批注是实现 JPA ...
- **常用参考**:提供了关于 JPA 和 MyEclipse 的常用参考链接,帮助读者深入学习。 - **其他参考**:列出了其他可能有用的参考资料,扩展学习范围。 #### 九、反馈 - **反馈机制**:鼓励读者提供反馈,以便持续...
JPA(Java Persistence API)是一种用于管理关系型数据库对象的标准API,它使得Java开发人员能够以面向对象的方式操作数据库。通过提供一套标准的数据持久化规范,JPA大大简化了数据访问层的开发工作。本文将详细...
总结来说,`Maven JPA 例子`是一个很好的学习资源,它展示了如何在Maven项目中集成JPA,使用Spring Data JPA简化数据库操作,以及如何在Eclipse环境中进行开发。这个例子可以帮助开发者理解Java世界中的ORM(对象...
Spring Boot集成了大量常用的第三方库配置,如JDBC、MongoDB、JPA等,开发者无需进行复杂的配置,即可快速创建独立的、生产级别的基于Spring的应用。 Spring Data JPA(Java Persistence API)是Spring框架的一个...
### Spring Data JPA中文文档知识点总结 #### 一、Spring Data JPA 概述 **Spring Data JPA** 是 Spring Data 项目的一部分,它提供了一种简单的方式来与 JPA(Java Persistence API)进行交互,旨在简化数据库...
它集成了大量常用的第三方库配置,如JDBC、MongoDB、JPA、RabbitMQ、Quartz等,使得开发者无需繁琐的配置就能快速启动项目。在课表排课及实验室机房管理系统中,SpringBoot可以作为基础框架,提供自动配置、内嵌...