一、JPA(Java持久化API)
JPA为JavaEE平台提供了ORM框架的规范。
JPA包含大量方便易用的API,可以使开发人员摆脱繁重的JDBC底层处理,以更加面向对象的方式进行持久化操作。
JPA操作的持久化对象称为实体。
- 实体、实体主键、实体与表的对应关系
- 实体的生命周期
- 持久化上下文的原理
- 实体管理器的持久化操作方法
3.1 JAP概述
- JPA(Java Persistence API,Java持久化API)为Java EE平台提供了ORM框架的规范。JPA是位于JDBC 之上的一个更高层抽象,包含大量方便易用的API,可以使开发人员摆脱繁重的JDBC底层处理,以更加面向对象的方式进行持久化操作。
3.1.1 ORM
-
ORM(Object-Relational Mapping,对象关系映射) 是解决对象和关系模型的不匹配问题的一种框架。
-
由于对象和关系模型采用了不同的数据组织形式,造成使用面向对象方式操作关系型的数据变得尤为困难。
ORM 的解决方法是:
(1)提供一种专门负责持久化的容器,预先配置映射关系,自动生成SQL语句,提高开发效率。
(2)由持久化容器提供可控制的加载策略和适当范围的缓存。
表3-1 对象和关系模型中的对应概念
Java面向对象模型 | 关系模型 |
---|---|
类 | 表 |
对象(实例) | 记录(行) |
状态(属性) | 字段(列) |
行为(方法) | 存储过程、触发器等于对象的方法基本对应 |
身份(引用) | 主键 |
对其他对象的引用 | 外键关系 |
继承和多态 | 不支持 |
3.1.2 JPA持久化提供器
同JDBC类似,JPA只是一个规范,并没有具体实现。但JPA是Java EE规范,所以符合Java EE规范的容器都应该提供JPA的具体实现,这称为JPA持久化提供器。(类似于JDBC驱动程序)
JBOSS默认使用Hibernate作为持久化提供器。
3.2 JPA实体
-
JPA操作的持久化对象称为实体(Entity),简单的POJO就可以被JPA持久化,但需要配置基本的映射关系。大部分情况下,实体对应于数据库中的表,实体的属性对应于表的列,实体的每个对象则对应表中的每条记录。
-
为了完成数据持久化,应用程序需要通过JPA中特定注解标识各种映射关系提供给JPA持久化提供器
-
JPA使用的所有注解都位于javax.persistence包下。
-
标识一个实体必须要有**@Entity** 和**@Id**两个注解,其他的映射关系提供默认行为。
3.2 JPA实体
3.2.1声明实体
只需在类上加上**@Entity注解即可声明为JPA**实体。
3.2.2实体主键
每个实体必须有一个主键,且是唯一的。JPA支持使用基本类型和Serializable类型的属性作为实体的主键。使用**@Id注解将实体的一个属性声明为主键。除了直接声明在属性上,通常更推荐在get方法上声明主键**。
3.2.3映射实体
映射表: @Table(name=“user”) 实体类前
映射列: @Column(name=“id”) get方法前
-
如果省略@Table和 @Column注解,则分别默认会被映射到与实体类同名的表以及与实体属性名称相同的列。
-
如果实体中存在不需要被映射的列时,必须使用@Transient注解进行显式的声明
3.2.4 生成主键值
-
JPA要求实体必须具有主键,实体的主键值可以在应用程序中指定,通常有数据库自动生成,使用@GeneratedValue注解指定主键值生成方式, 主要取决于GenerationType的四个值:
-
身份列(GenerationType.IDENTITY)
-
序列(GenerationType.SEQUENCE)
-
表生成器(GenerationType.TABLE)
默认策略(GenerationType.AUTO)
Produce.java
@Entity
@Table(name="product")
@SequenceGenerator(name="productSequence",
sequenceName="pd_seq")
public class Product {
public Integer id;
String name;
String code;
Double minStock;
@Id
@Column(name="id")
@GeneratedValue(strategy=GenerationType.SEQUENCE,
generator="productSequence")
public Integer getId() {
return id; }
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public Double getMinStock() {
return minStock;
}
public void setMinStock(Double minStock) {
this.minStock = minStock;
}
如果实体对象需要返回客户端,必须实现Serializable 接口。 |
---|
@Entity 注释指明这是一个实体Bean。 |
@Table 注释指定了实体Bean 所要映射的表。 |
@Id 注释指定实体的主键。每个实体 必须有一个主键,并且必须是唯一的。 |
@Column 注释指定实体的成员属性映射到数据表中的哪一个字段。 |
@GeneratedValue 注解指定主键值生成方式,该注解与@Id 注解结合使用在主键属性上。 |
3.3 实体管理器
JPA提供了大量方便易用的API用于实体操作,通过EntityManager可以实现对实体的添加、更新、删除及查询等功能。
3.3.1 EntityManager接口
EntityManager接口是JPA的核心接口,定义了功能丰富的持久化方法,比如,persist()、remove()、find() 、getReference()、merge()、reflesh() 和flush()等。
3.3.2持久化上下文
-
持久化上下文是指受EntityManager管理的实体实例的集合。 EntityManager跟踪着持久化上下文中每个实例的修改和更新情况,负责将实例数据的改变保存到数据库中。
持久化上下文主要有两种:
-
事务范围的持久化上下文
-
扩展的持久化上下文(有状态会话Bean)
3.3.3实体生命周期
实体的状态
实体对象拥有以下4个状态,这些状态通过调用EntityManager 接口方法发生迁移:
-
瞬时状态:新创建的实体对象,尚未拥有持久化主键,没有和一个持久化上下文关联起来。
-
**托管状态:**已经拥有持久化主键并和持久化上下文建立了联系;调用clear()方法会把对象分离开来,成为游离状态。
-
游离状态:拥有持久化主键,但尚,未和持久化上下文建立联系;可以调用em.merge()方法,这个方法会根据实体类的id来更新数据库数据,这时实体类变成了托管状态。
-
删除状态:拥有持久化主键,已经和持久化上下文建立联系,但已经被安排从数据库中删除。
3.3.4持久化单元
持久化单元(persistence unit) 是EntityManager 负责管理的一组实体类。
persistence unit 是在persistence.xml 中定义的。根据持久化规范的要求,该部署描述文件是必须提供的,将其存放在项目的META-INF 目录中。 容器管理的EntityManager通过**@PersistenceContext** 注解动态注入EntityManager 实例。
实例:产品实体的持久化操作
业务接口( ProductServiceLocal.java )
@Local
public interface ProductServiceLocal {
public int getProductsNumber(); //获取产品数量
List<Product> getProducts(int pageIndex,int pageSize);//分页查询产品
Product getProductById(int id); //查询单个产品
void addProduct(Product product); //添加产品
void updateProduct(Product product); //更新产品
void removeProduct(Product product); //删除产品
}
产品会话Bean(ProductServiceBean.java)
@Stateless
public class ProductServiceBean implements ProductServiceLocal {
@PersistenceContext(unitName="jpademo")
private EntityManager em;
public Product getProductById(int id) {
return em.find(Product.class, id);
}
public void addProduct(Product product) {
em.persist(product);
}
public void updateProduct(Product product) {
em.merge(product);
em.flush();
}
public void removeProduct(Product product) {
Product p = em.find(Product.class, product.getId());
em.remove(p);
em.flush();
}
}
3.3.5持久化操作
-
find()方法用于查找特定主键的实体bean。
-
persist()方法用于将实体保存到数据库中。
-
merge()方法用于更新或保存实体(当实体不存在时,执行保存操作当实体已经存在时,执行更新操作)。
-
remove()方法用于删除实体。
-
flush ():同步持久上下文环境,即将持久上下文环境的所有未保存实体的状态信息保存到数据库中。
-
refresh (Object entity):用数据库实体记录的值更新实体对象的状态,即更新实例的属性值。
刷新实体refresh()
如果你怀疑当前托管对象存放的并非数据库中最新的数据时(如:在你获取了实体对象之后,在数据库中的记录被修改了),你可以通过refresh()方法刷新实体对象。容器会把数据库中的新值重写进实体对象。
@PersistenceContext protected EntityManager em;
…
Produce p= em.find(Prodcue.class, 2);
//如果此时p对应的记录在数据库中已经发生了改变,可以通过refresh()方法得到最新数据。
em.refresh §;
刷新flush()
当你调用EntityManager.persist( )、EntityManager.merge( )、EntityManager.remove( ) 或托管对象setter 这些方法时,实体的状态并不会立刻同步到数据库中,直到EntityManager 决定flush 时,才会把实体的状态同步到数据库。这样做的目的是为了减少跟数据库交互的次数,以提高应用的性能。容器会将所有数据库操作集中到一个批处理中,并在某个时刻提交到数据库。
发布前的检查工作
- 数据源是否配置,数据库是否已经启动。
- persistence.xml 文件是否存在于jar 文件的META-INF 目录,文件中是否指定了数据源。
数据源以及persistence.xml 文件配置详见任务4的文档。
检查工作完毕,就可以把EJB项目 部署到jboss 中。
查看表
当实体类发布成功后,我们可以查看数据库中生成的Produce 表,如下图:
任务4:产品实体的持久化操作
1. 配置数据源
(1)为WildFly 10.x添加连接MySQL所依赖的jar包(MySQL驱动程序),进入目录%JBOSS_HOME%/modules,在该目录下创建/mysql/main,之后将mysql-connector-java-5.1.18.jar 放入main文件夹,然后在该main文件夹下创建配置文件module.xml,并将以下代码贴进去:
如果使用的是其他版本的jar的话,请自行修改resource-root标签的path属性为对应版本的jar包的文件名。
(2)修改standalone.xml配置
打开文件%JBOSS_HOME%/standalone/configuration/standalone.xml,全文搜索”datasource”,可以找到默认的数据源h2的配置,如下所示:
在标签中添加一个子标签用于配置mysql数据源,同时在标签新增一个子标签用于指定mysql对应的模块以及驱动类,新增代码如下:
标签中的url中的mytest为要连接的数据库名,与 标签为MySQL数据库的账户名与密码。
(3)启动JBoss服务器,进入JBoss管理界面,Configure----DataSource ,便可查看到新增的jndi名称为:java:jboss/datasources/MySQLDS
2. 创建EAR项目(企业级应用项目)
(1)创建 EAR项目,目录结构如下: