2、JPA连接工厂
通过之前的 代码 实现已经清楚的发现了整个的JPA实现步骤,但是这个步骤似乎有一些繁琐了,毕竟最终所关心的一定是EntityManager对象实例,而要想获取到此对象的实例,那么要经过许多的步骤,这样如果每一次都重复的进行处理,会非常的繁琐了,那么就需要进行代码的抽象规定。
// 创建JPA Entity工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("YootkJPA"); // JPA操作对象 EntityManager entityManager = factory.createEntityManager();
对等概念: DataSource -> EntityMannagerFactory
Connection -> EntityMannager,每一个对象的实例都表示一个Session的操作
所以此时可以考虑将部分的代码移交给JPA的专属连接管理类,用这个类可以基于ThreadLocal实现EntityManager存储,这样每一次通过该类的方法获取EntityManager的时候如果不关闭,则获取到的是同一个实例。
对于上叙测试类的优化。
- 一个工具类,简化EntityManagerFactory的创建与关闭
public class JPAEntityFactory { /** * JPA持久单元 */ private static final String PERSISTENCE_UNIT = "YootkJPA"; /** * 等同于 数据源 */ private static EntityManagerFactory entityManagerFactory; /** * EntityManager 等同于 连接 */ private static ThreadLocal<EntityManager> entityManagerThreadLocal = new ThreadLocal<>(); static { // 初始化创建数据源 静态代码块只会创建一次 rebuildEntityManagerFactory(); } /** * 获取连接 */ public static EntityManager getEntityManager() { // 本机线程获取连接 EntityManager entityManager = entityManagerThreadLocal.get(); if (entityManager == null) { // 从数据源获取新的连接 entityManager = getEntityManagerFactory().createEntityManager(); // 存入本地线程 entityManagerThreadLocal.set(entityManager); } return entityManager; } private static EntityManagerFactory getEntityManagerFactory() { if (entityManagerFactory == null) { // 创建数据源 rebuildEntityManagerFactory(); } return entityManagerFactory; } private static void rebuildEntityManagerFactory() { // 持久单元 创建 数据源 entityManagerFactory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT); } /** * 关闭EntityManager */ public static void close() { // 获取实例 EntityManager entityManager = entityManagerThreadLocal.get(); if (entityManager != null) { // 关闭连接 entityManager类似于Connection entityManager.close(); // 删除本地线程中的连接 entityManagerThreadLocal.remove(); } } }
修改测试类:
@Test public void testAdd2() { // 获取连接 EntityManager entityManager = JPAEntityFactory.getEntityManager(); // 开启事务 entityManager.getTransaction().begin(); Course course = new Course(); course.setCname("Spring编程实战"); // 字符串 日期对象 course.setStart(DateUtil.stringToDate("2022-09-19")); course.setEnd(DateUtil.stringToDate("2022-12-30")); course.setCredit(2); course.setNum(88); // 执行插入 entityManager.persist(course); // 提交事务 entityManager.getTransaction().commit(); // 关闭连接 JPAEntityFactory.close(); }
3、主键生成
在JPA开发之中,主键数据的生成主要是基于
@Id注解定义的,而在实际的项目开发之中,数据表的设计结构是有所不同的,所以JPA为了适应这些不同的数据表的定义,也提供有不同的主键生成策略。

3、DDL自动更新
在实际的开发之中你是否会出现这样的一种比较 尴尬 的问题,在进行开发的时候有人修改数据表,而后当前的实体类结构和数据表的结构不统一,但是在JPA设计的时候,充分的考虑到了这种数据表修改的问题(表可能存在,也可能不存在,或者表的结构可能修改了),所以在这样的环境下就需要让代码可以自动的进行数据表的纠正。
在传统的项目开发之中,常规的做法是先进行数据表的创建,而后在围绕数据表进行业务功能的实现。在每次业务发生改变时,也是先进行表结构的修改,而后再进行程序的变更,这样的数据库维护是非常繁琐的,考虑到数据库更新以及 数据库移植 方面的设计,在 Hibernate 之中提供了 DDL 自动创建以及表更新策略

JPA现在主要是基于 Hibernate 实现,那么 Hibernate 开发框架最早的一个特点就在于 可移植性,也就是说一个项目是在MySQL数据库下开发的,那么通过简单的配置修改,可以让代码直接在Oracle数据库中运行。
对于当前市面上可以见到的ORM开发框架来讲,只有JPA标准规定了数据库移植性的话题,而Hibernate 实现了JPA标准,所以只有Hibernate 开发框架具有移植性的功能,而像大家所熟悉的MyBatis是没有这样的功能。
DDL更新策略

3.1、使用
去到JPA配置文件中,修改DDL更新策略、
3.1.1、create
每次加载时,根据实体类生成表,如果表存在于数据库,会先删除
<!-- JPA更新策略 --> <property name="hibernate.hbm2ddl.auto" value="create"/>

查看执行日志信:可以看到 drop table if exists course (删除表,然后会依据实体类,重新创建表)
3.1.2、update
如果表不存在,重建表。
存在:如:实体类某个字段,在数据表中不存在,这个时候会添加。但是,删除实体类的某个字段,数据库对应的字段并不会删除。
修改实体类: 添加教师

修改配置文件-更新策略为update

查看执行日志:
表不存在,创建表
create table course (
cid bigint not null auto_increment,
cname varchar(255),
credit integer,
end date,
num integer,
start date,
teacher varchar(255),
primary key (cid)
) engine=InnoDB
排除属性
假如,我们相使实体类中的某个字段,在执行时,不创建数据库中的对应字段。使用@Transient即可
