前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >实战:应用对持久数据访问| 从开发角度看应用架构9

实战:应用对持久数据访问| 从开发角度看应用架构9

作者头像
魏新宇
发布2018-07-30 14:33:56
1.6K0
发布2018-07-30 14:33:56
举报

一、前言

本文仅代表作者的个人观点;

本文的内容仅限于技术探讨,不能作为指导生产环境的素材;

本文素材是红帽公司产品技术和手册;

本文分为系列文章,将会有多篇,初步预计将会有16篇。

二、Java对持久数据的访问方式

前文已经提到,Java应用对应用数据的访问,最终通过ORM方式实现。

而ORM的实现,通过JPA的标准,底层使用Hibernate等技术。

JPA中的几个重要的API:

JPA的API有主要以下几个:实体(entity)、持久性单元(persistence units)、持久性上下文( persistence context)、Entity Manager。我们先看Entity Manager。

几者之间的关系:

一个entity其实就是一个class,只是定了与数据库表的对应。如上图,class叫大魏,数据库中也有一张表叫大魏(类的名称可以和数据库表名不同,使用@Table指定即可)。

大魏这个类,在被生成对象时,会从数据库表中读数据,然后可能会对数据修改,修改的这些数据,会存到持久性上下文中(运行在内存中),在默写情况下,会被存回数据库表中(例如提交)。

java对数据库表的操作,实际上是使用entity manager调用CRUD完成的。而entity manager之所以能对数据库做操作,是因为其底层调用Hibernate,封装了JDBC。而Hibernate相关定义的静态配置,是存放到persistence units中的。

(默认模式下)entity manager是运行到EJB container中,也就是中间件中的。

EntityManagerFactory

EntityManagerFactory 接口主要用来创建 EntityManager 实例。EM 是一个接口,创建的话要 new 它的实现类,工厂类里有好多静态方法,调运时返回一个 EM

EntityManagerFactory该接口约定了如下4个方法:

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

三、实体类对数据的两种访问方式

实体类与标准POJO类相似,但实体有几个重要的区别,需要由EntityManager进行管理。 要将POJO类转换为实体,请在类头中添加@Entity注释。 另外,应该通过使用getter和setter方法来访问每个实例变量。 最后,类必须至少有一个没有参数的构造函数,尽管类仍然可以有其他构造函数接受参数。

以下是一个实体类的例子:

代码语言:javascript
复制
@Entity

public abstract class Customer {

     @Id
     private int custId;
     private String custName;
     ....
     public Customer(){ } // No argument constructor

     //setter and getter methods
     public String getCustName() {
       return custName;
     }
     public void setCustName(String custName) {
       this.custName = custName;
     }

     ...
} 

实体字段和属性

实体类中的非瞬态数据会持久保存到数据库表中。 JPA提供者既可以将数据库表中的数据加载到实体类中,也可以将实体类中的数据存储到数据库表中。 提供者访问状态的方式称为访问模式。 有两种访问模式:基于字段的访问和基于属性的访问。

基于字段的访问Field-based:

这种方式是:通过注释字段提供基于字段的访问。 实体类中的持久字段必须声明为私有,受保护或包级别访问。 持久字段是以下类型之一:

  • Java primitive types: byte, short, int, long, or char
  • java.lang.String type
  • Java Wrapper classes for primitive types: Byte, Short, Integer, Long, or Character
  • Temporal types: java.util.Date, or java.sql.Date
  • Enumerated types
  • Embeddable classes, other entities, and collections of entities

基于字段的访问的示例如下所示:

代码语言:javascript
复制
@Entity
public class Customer implements Serializable {
     // Note that the fields are annotated
     @Id
     protected int custId;
     protected String custName;
     @Temporal(TemporalType.DATE)
     protected Date registrationDate;     
     @Column(name="address")
     protected Address custAddress;
     .....
}

基于字段的访问提供了额外的灵活性。

基于属性的访问--Property-based Access

为了提供基于属性的访问,getter和setter方法必须在Java实体类中定义。因为只能通过方法访问,可以说基于属性的访问提供了更好的封装。 通过注解getter方法提供基于属性的访问。 getter方法的返回类型决定了属性的类型。 getter方法的返回类型必须与传递给setter方法的参数的类型相同。 getter和setter方法必须是public或protected,并且必须遵循Java bean的命名约定。 基于属性的访问的一个例子如下:

代码语言:javascript
复制
@Entity
public class Customer implements Serializable {
     protected int custId;
     protected String custName;
     protected Date registrationDate;
     protected Address custAddress;
     ....
     //Note the getter methods are annotated     @Id
     public int getCustId(){
        return custId;
     }
     public String getCustName(){
        return custName;
     }
     @Temporal(TemporalType.DATE)
     public Date getRegistrationDate(){
        return registrationDate;
     }
     @Column(name="address")
     public Address getCustAddress(){
        return custAddress;
     }
     ....
     //Setter methods
}

四、实体的四种状态

实体的四种类型:

New State: 使用Java新运算符创建的实体实例处于新状态或瞬态状态。 实体实例不具有持久性标识,并且尚未与持久性上下文相关联。

Managed State:具有持久性标识、并与持久性状态关联的实体实例、处于受管状态或持久状态。 当对管理实体字段中的数据进行更改时,它将与数据库表数据同步。 应用程序调用实体管理器的持久性,查找或合并方法后,实体实例处于受管状态。

Removed State:持久实体可以通过多种方式从数据库表中删除。 当提交事务或调用实体管理器的remove方法时,可以从数据库表中删除一个托管实体实例。 一个实体然后处于移除状态。

Detached State: 实体具有持久性实体标识,但不与持久性上下文相关联。 当实体被序列化或在事务结束时会发生这种情况。 这种状态被称为实体的分离状态。

五、EntityManager接口和关键方法

javax.persistence.EntityManager接口用于与持久性上下文进行交互。 实体实例及其生命周期在持久性上下文中进行管理。

javax.persistence.EntityManager API用于创建新的实体实例,通过主键查找实体实例,通过实体实例进行查询以及删除现有的实体实例。 EntityManager的关键方法是:

persist()方法持久化一个实体并使其得到管理。 persist()方法在数据库表中插入一行。 如果persist操作失败,persist()方法将抛出PersistenceException。

代码语言:javascript
复制
@Stateless
public class CustomerServices {
       ....
       public void saveCustomer(Customer customer) {
           ...
           try{ entityManager.persist(customer);
           }catch(PersistenceException persistenceException){
               // code to handle PersistenceException
           }
       }
}

find()方法通过主键搜索特定类的实体并返回一个托管实体实例。 如果找不到对象,则返回null。

代码语言:javascript
复制
@Stateless
public class CustomerServices {
       ....
       public void getCustomer(Customer customer) {
           ...
           Customer customer;
           try{
               customer = entityManager.find(Customer.class,custId);
               if (customer != null){
                 System.out.print(customer.getCustName());
               } else }
                   System.out.print("Not Found");
               }
           }catch(Exception exception){
               // code to handle PersistenceException
           }
       }
}

contains()方法将一个实例作为参数并检查实例是否在持久化上下文中:

代码语言:javascript
复制
@Stateless
public class CustomerServices {
       ....
       public boolean saveCustomer(Customer customer) {
           ...
           entityManager.persist(customer);
           return entityManager.contains(customer);
       }
}

merge()方法更新现有分离实体的表中的数据。 merge()方法为处于新状态或瞬态状态的实体在数据库表中插入新行。 合并操作之后,实体处于受管理状态。

代码语言:javascript
复制
@Stateless
public class CustomerServices {
       ....
       public void updateCustomer(Customer customer) {
           ...
           Customer customer;
           try{
               customer = entityManager.find(Customer.class,custId);               entityManager.merge(customer);
           }catch(Exception exception){
               // code to handle PersistenceException
           }
       }
}

remove()方法删除一个托管实体。 要删除分离的实体,请调用一个返回受管实例的find()方法,然后调用remove()方法。

代码语言:javascript
复制
@Stateless
public class CustomerServices {
       ....
       public void deleteCustomer(Customer customer) {
           ...
           Customer customer;
           try{
               customer = entityManager.find(Customer.class,custId);               entityManager.remove(customer);
           }catch(Exception exception){
               // code to handle PersistenceException
           }
       }
  }

clear()方法清除持久性上下文。 完成此操作后,所有受管实体都处于分离状态。

代码语言:javascript
复制
     ...
             try{                 entityManager.clear();
             }catch(Exception exception){
                 // code to handle PersistenceException
             }

refresh()方法从数据库表中刷新实体实例的状态。 实体实例中的当前数据被从数据库表中提取的数据覆盖。

代码语言:javascript
复制
     ...
             try{                 entityManager.refresh(customer);
             }catch(Exception exception){
                 // code to handle PersistenceException
             }

persistence.xml文件是一个包含持久性单元的标准配置文件。 每个持久性单元都有一个唯一的名称。

1持久性单元名称是持久性单元的名称。持久性单元的名称用于获取EntityManager。

2事务类型可以是JTA或RESOURCE_LOCAL。事务类型定义了应用程序打算执行什么类型的事务。容器事务使用每个Java EE应用程序服务器中提供的Java事务API(JTA)。在JTA类型的事务中,容器负责创建和跟踪实体管理器。在RESOURCE_LOCAL中,您负责创建和跟踪实体管理器。

3jta-data-source是数据源的名称。每个持久性单元都必须有一个数据库连接。 JPA提供程序在启动时使用JNDI查找服务按名称查找数据源。

4可以在属性元素中设置其他标准或特定于供应商的属性。 hibernate.Dialect属性指定使用哪个数据库。具有更新值的hibernate.hbm2ddl.auto属性会自动更新模式。具有值为true的hibernate.show-sql属性可以将SQL语句记录到控制台。

六、实战:应用对持久数据的访问

通过JBDS导入一个已经存在maven项目:

查看一个包的 com.redhat.training.model java的源码: Person.java

修改源码如下位置:

增加import库并 @Entity注释:

通过以上操作,将一个普通的POJO变成了Entity。

Person实体类必须实现Serializable接口。 导入并实现Serializable接口。

将@Id和@GeneratedValue(strategy = GenerationType.IDENTITY)注释添加到Person类的id属性中,使其成为主键,并将其生成为IDENTITY。

将@Column(name =“name”)注释添加到personName属性,以将其映射到数据库表中的名称字段。 导入所需的库。

在com.redhat.training.services包中打开PersonService类并添加持久性功能以将Person保存到数据库并从数据库中查找人员。

需要EntityManager对象来执行PersonService类中的持久性操作。 添加@PersistenceContext注释以获取EntityManager对象:

使用实体管理器将Person持久化到数据库中,将以下代码添加到公共String hello(String name)方法中,如下所示:

找到使用id的人的名字,将方法getPerson(Long id)添加到PersonService类。 在return语句中,使用实体管理器的find()方法根据id返回Person的name属性。

观察getAllPersons()方法,该方法返回存储在数据库中的所有Person对象:

在com.redhat.training.ui包中打开Hello类。 取消注释getPerson()和getPersons()方法,以添加前端功能以查看存储在数据库中的单个人员姓名和所有姓名。

修改为:

启动EAP:

接下来,构建和部署应用。

接下来,在EAP上部署应用:

部署成功:

通过浏览器访问应用:

输入名字:david wei,点击提交:

点击view all names:

说明姓名已经被insert到数据库表中。

参考文档:

  1. 红帽技术文档
  2. https://blog.csdn.net/qq_24084925/article/details/51890054

魏新宇

  • 红帽资深解决方案架构师
  • 专注开源云计算、容器及自动化运维在金融行业的推广
  • 拥有MBA、ITIL V3、Cobit5、C-STAR(云安全评估师)、TOGAF9.1(鉴定级)等管理认证。
  • 拥有红帽RHCE/RHCA、VMware VCP-DCV、VCP-DT、VCP-Network、VCP-Cloud、AIX、HPUX等技术认证。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-06-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 大魏分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档