写点什么

原创 | 使用 JPA 实现 DDD 持久化 - 启动 JPA 程序 + 通过 JPA 原生 API 访问数据

发布于: 2020 年 12 月 18 日
原创 | 使用JPA实现DDD持久化-启动JPA程序+通过JPA原生API访问数据

启动JPA程序



本项目中,使用Hibernate作为隐藏在JPA幕后的ORM框架。



Java EE环境中,由应用服务器负责启动JPA。包含持久化单元(内含实体、值对象、映射元数据和persistence.xml文件)的工件(warear存档文件)被部署到应用服务器之后,应用服务器会扫描持久化单元,读取persistence.xml文件和映射元数据,据此启动JPA程序。



Java SE环境下,由应用代码负责启动JPA。基本步骤如下:



1、通过Persistence类的静态工厂方法createEntityManagerFactory()创建EntityManagerFactory,传入持久化单元的名字作为参数(持久化单元的名字在/META-INF/persistence.xml文件中定义)。



EntityManagerFactory emf = Persistence.createEntityManagerFactory("default");



2、通过EntityManagerFactorycreateEntityManager()方法创建EntityManager



EntityManager entityManager = emf.createEntityManager();



3、使用EntityManager进行各种持久化和查询操作。例如:



EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
String jpql = "select o from Order o where o.buyer = :buyer";
List<Order> results = entityManager.createQuery(jpql, Order.class)
.setParameter("buyer", buyer)
.getResultList();
transaction.commit();
entityManager.close();



通过JPA原生API访问数据



JPA的核心API有这么几个:



  • EntityManagerFactory



系统中针对每个持久化单元创建一个对应的EntityManagerFactory,它是EntityManager的工厂。这个对象的创建是重量级的,同时也是线程安全的。一般在程序中是一次创建,重复使用,直至应用结束时才关闭。



  • EntityManager



实体管理器EntityManagerJPA API核心中的核心,从EntityManagerFactory对象创建,代表对数据库的一次会话。它可以直接执行实体的增、删、改操作,并通过创建Query对象执行实体查询操作。它的创建是轻量的,但不是线程安全的,应该按需创建,及时关闭。



  • EntityTransaction



对数据库的操作必然涉及事务问题。在Java EE环境中,事务一般是由应用服务器容器管理的,应用代码可以不用考虑事务。但在Java SE环境中,需要由应用代码来负责开始和提交/回滚事务。通过EntityManager对象的getTransaction()方法获取事务对象EntityTransaction,在持久化操作开始前调用EntityTransactionbegin()方法开始一个事务,操作成功完成后调用它的commit()方法提交事务,操作失败时调用它的rollback()方法进行事务回滚。



  • Query



要对数据库执行查询,就需要通过EntityManager对象的createQuery()方法创建Query对象,接受一个JPA查询语言写成的查询字符串作为参数。查询可以返回单个结果(调用Query对象的getSingleResult()方法),一个列表(调用Query对象的getResultList()方法),一个流(调用Query对象的getResultStream()方法)等。



Query还有一个变体TypedQuery,支持泛型。当给EntityManager对象的createQuery()方法传入一个类对象作为第二个参数时将返回一个TypedQuery对象。它能够返回适当类型的对象或泛型列表,而不需要强制类型转换。



下面是tmall-persistence-jpa模块中的范例代码,OrderRepository类用原生JPA API实现了订单仓储接口Orders



public class BuyerRepositoryJpql implements Buyers {
private final EntityManager entityManager;
public BuyerRepositoryJpql(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Override
public <T extends Buyer> T save(T buyer) {
return entityManager.merge(buyer);
}
@Override
public void delete(Buyer buyer) {
entityManager.remove(buyer);
}
@Override
public List<Buyer> findAll() {
return entityManager.createQuery("select o from Buyer o").getResultList();
}
@Override
public Optional<Buyer> getById(int id) {
return Optional.ofNullable(entityManager.find(Buyer.class, id));
}
@Override
public Optional<Buyer> getByName(String name) {
return entityManager
.createQuery("select o from Buyer o where o.name = :name", Buyer.class)
.setParameter("name", name)
.getResultStream()
.findAny();
}
@Override
public Stream<Buyer> findByNameStartsWith(String nameFragment) {
return entityManager
.createQuery("select o from Buyer o where o.name Like :name", Buyer.class)
.setParameter("name", nameFragment + "%")
.getResultStream();
}
@Override
public Stream<Buyer> findByNameContains(String nameFragment) {
return entityManager
.createQuery("select o from Buyer o where o.name Like :name", Buyer.class)
.setParameter("name", "%" + nameFragment + "%")
.getResultStream();
}
@Override
public Optional<PersonalBuyer> findPersonalBuyerByQQ(String qq) {
String jpql = "select o from PersonalBuyer o join o.imInfos i where KEY(i) = :key and VALUE(i) = :value";
return entityManager.createQuery(jpql, PersonalBuyer.class)
.setParameter("key", ImType.QQ)
.setParameter("value", qq)
.getResultStream()
.findAny();
}
}



我们编写集成测试类作为仓储的客户来调用仓储的持久化代码:



public abstract class BaseIntegrationTest implements WithAssertions {
private static EntityManagerFactory emf;
protected EntityManager entityManager;
private EntityTransaction transaction;
@BeforeAll
static void beforeAllTest() {
emf = Persistence.createEntityManagerFactory("default");
}
@BeforeEach
void BeforeEachTest() {
entityManager = emf.createEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
}
@AfterEach
void afterEachTest() {
transaction.rollback();
entityManager.clear();
}
@AfterAll
static void afterAllTest() {
emf.close();
}
}
class BuyerRepositoryJpqlTest extends BaseIntegrationTest {
private static final String buyer1Name = "张三";
private static final String buyer2Name = "华为公司";
private Buyers buyers;
private PersonalBuyer buyer1;
private OrgBuyer buyer2;
@BeforeEach
void beforeEach() {
buyers = new BuyerRepositoryJpql(entityManager);
buyer1 = buyers.save(new PersonalBuyer(buyer1Name));
buyer2 = buyers.save(new OrgBuyer(buyer2Name));
}
@AfterEach
void afterEach() {
buyers.findAll().forEach(buyers::delete);
}
@Test
void findById() {
assertThat(buyers.getById(buyer1.getId())).containsSame(buyer1);
assertThat(buyers.getById(buyer2.getId())).containsSame(buyer2);
}
@Test
void findByName() {
assertThat(buyers.getByName(buyer1Name)).containsSame(buyer1);
assertThat(buyers.getByName(buyer2Name)).containsSame(buyer2);
}
@Test
void findByNameStartsWith() {
assertThat(buyers.findByNameStartsWith("华"))
.contains(buyer2)
.doesNotContain(buyer1);
assertThat(buyers.findByNameStartsWith("三"))
.isEmpty();
}
@Test
void findByNameContains() {
assertThat(buyers.findByNameContains("三"))
.contains(buyer1)
.doesNotContain(buyer2);
}
@Test
void findAll() {
assertThat(buyers.findAll()).contains(buyer1, buyer2);
}
@Test
void delete() {
buyers.delete(buyer1);
assertThat(buyers.findAll()).contains(buyer2).doesNotContain(buyer1);
}
@Test
void update() {
buyer1.setName("李四");
buyers.save(buyer1);
assertThat(buyers.getById(buyer1.getId()).map(Buyer::getName)).containsSame("李四");
assertThat(buyers.getById(buyer2.getId()).map(Buyer::getName)).containsSame(buyer2Name);
}
@Test
void findPersonalBuyerByQQ() {
buyer1.setImInfo(ImType.QQ, "34567");
buyers.save(buyer1);
assertThat(buyers.findPersonalBuyerByQQ("34567")).containsSame(buyer1);
}
}



总结:在Java SE环境中通过JPA原生API访问数据的步骤:



  1. 在应用启动时,针对每个持久化单元一次性创建EntityManagerFactory对象并缓存起来;

  2. 对数据库的每一次访问:

  3. EntityManagerFactory对象中生成一个EntityManager新实例;

  4. 调用EntityManagergetTransaction()方法获得EntityTransaction事务对象;

  5. 调用EntityTransactionbegin()方法开始一个新的事务;

  6. try...catch...语句内部调用EntityManager的方法进行增删改查操作;

  7. 当数据访问方法成功完成,调用EntityTransactioncommit()方法提交事务;

  8. 如果数据访问方法抛出异常,调用EntityTransactionrollback()方法回滚事务;

  9. 调用EntityManager对象的close()方法关闭它;

  10. 当整个应用结束运行之前,调用EntityManagerFactory对象的close()方法关闭它。



详细内容请戳这里↓↓↓



原创 | 使用JPA实现DDD持久化-启动JPA程序+通过JPA原生API访问数据



这一节就讲到这里,下一节我们讲"通过Spring Data JPA访问数据"



如果觉得有收获,点个【赞】鼓励一下呗!





发布于: 2020 年 12 月 18 日阅读数: 10
用户头像

高级架构师,技术顾问,交流公号:编程道与术 2020.04.28 加入

杨宇于2020年创立编程道与术,致力于研究领域分析与建模、测试驱动开发、架构设计、自动化构建和持续集成、敏捷开发方法论、微服务、云计算等顶尖技术领域。 了解更多公众号:编程道与术

评论

发布
暂无评论
原创 | 使用JPA实现DDD持久化-启动JPA程序+通过JPA原生API访问数据