Java单元测试实践-26.使用JPA自动创建数据库表

Java单元测试实践-00.目录(9万多字文档+700多测试示例)
https://blog.csdn.net/a82514921/article/details/107969340

1. 使用JPA自动创建数据库表

以上在单元测试中使用H2数据库的方法,需要在连接数据库时指定数据库建表语句,建表语句需要从数据库服务器获取,由于不同的数据库建表语句存在一些差别,可能并不能直接在H2数据库中使用(如前文所述MySQL建表语句中H2数据库中存在部分不支持),不是最佳的选择。

可以在单元测试中使用JPA,利用JPA根据Entity自动创建数据库表的功能,屏蔽不同数据库的建语句的差异,简化处理H2数据库建表语句的操作。

1.1. JPA相关

1.1.1. 生成JPA Entity

JPA相关资料整理及根据数据库表生成JPA Entity的Java组件增强版使用说明,可参考 https://github.com/Adrninistrator/jpa-entity-generator-enhance ,在开源项目的基础上进行了优化。

1.1.2. EntityManager

参考ejb-3_0-fr-spec-persistence.pdf,“3.1 EntityManager”。

EntityManager实例与持久性上下文关联。持久性上下文是一组Entity实例,对于任何持久性Entity标识,都有一个唯一的Entity实例。持久性上下文对Entity实例及其生命周期进行管理。EntityManager接口定义了用于与持久性上下文进行交互的方法。EntityManager API用于创建和删除持久Entity实例,通过主键查找Entity,以及查询Entity。

1.1.3. EntityManagerFactory

参考ejb-3_0-fr-spec-persistence.pdf,“5.4 The EntityManagerFactory Interface”。

应用程序使用EntityManagerFactory接口来获取应用程序管理的EntityManager。当应用程序使用完EntityManagerFactory后,在应用程序关闭时,应关闭EntityManagerFactory。一旦EntityManagerFactory关闭,则其所有EntityManager都被视为处于关闭状态。

1.1.4. JPA自动建表参数配置

Hibernate实现了JPA规范。参考 https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#schema-generation 。

Hibernate允许从Entity映射生成数据库。

schema自动生成的功能对于测试非常有用,但不建议在生产环境中使用。

从Entity映射生成模式的过程称为HBM2DDL。

参考 https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#configurations-hbmddl 。

hibernate.hbm2ddl.auto参数用于设置为在SessionFactory生命周期中自动执行SchemaManagementTool操作。支持的参数值如下所示:

参数值说明
none不执行任何操作(默认值)
create-only创建数据库
drop删除数据库
create先删除数据库,再创建数据库
create-drop在SessionFactory创建时删除schema并重新创建,当SessionFactory关闭时删除schema
validate验证数据库schema
update更新数据库schema

经测试,设置hibernate.hbm2ddl.auto=update,在应用启动时,若数据库表不存在时会创建;对于已存在的数据库表,缺失的字段会增加,多余的字段不会删除。

1.1.5. 自动建表时打印SQL语句

参考 https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#_sql_statement_logging 。

当hibernate.show_sql参数值为true时,会在控制台打印所有的SQL语句,该参数默认值为false。

当hibernate.format_sql参数值为true时,会在日志和控制台中以更直观的方式打印SQL。

1.1.6. hibernate依赖组件

参考 https://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html_single/#d0e215 。

兼容JPA 2.0的Hibernate EntityManager建立在Hibernate core和Hibernate Annotations之上。从3.5版开始,将所有必需的模块捆绑在一个Hibernate发行版中:

模块说明
Hibernate Core原生Hibernate API和核心引擎
Hibernate Annotations基于注释的映射
Hibernate EntityManagerJPA 2.0 API和livecycle语义实现

可以添加对org.hibernate:hibernate-entitymanager的依赖,所有必须的依赖例如hibernate-core与hibernate-annotations会被传递地拉取。

org.hibernate:hibernate-entitymanager新版(5.4.11.Final及之后)已被移动至org.hibernate:hibernate-core,可使用org.hibernate:hibernate-core:5.4.12.Final。

由于test模块中的Entity使用了Lombok,因此还需要添加依赖“testAnnotationProcessor ‘org.projectlombok:lombok:1.18.12’”。

1.1.7. Spring JPA配置

1.1.7.1. Spring JPA

参考 https://docs.spring.io/spring-framework/docs/4.3.26.RELEASE/spring-framework-reference/htmlsingle/#orm-jpa 。

Spring JPA可以使用org.springframework.orm.jpa包,对JPA提供了全面的支持,通过与Hibernate或JDO集成的方式。

Spring JPA提供了三种设置JPA EntityManagerFactory的方式,应用程序会使用其获取EntityManager。包括LocalEntityManagerFactoryBean、从JNDI获取EntityManagerFactory、LocalContainerEntityManagerFactoryBean。

1.1.7.2. JpaVendorAdapter

参考 https://docs.spring.io/spring-framework/docs/4.3.26.RELEASE/spring-framework-reference/htmlsingle/#orm-jpa-dialect 。

JpaDialect实现可以启用Spring支持的一些高级功能,通常以特定于供应商的方式。

JpaVendorAdapter是主要用于Spring的全功能LocalContainerEntityManagerFactoryBean设置的更广泛的提供程序适配器装置。JpaVendorAdapter将JpaDialect的功能与其他特定于提供程序的默认值结合在一起。指定HibernateJpaVendorAdapter或EclipseLinkJpaVendorAdapter分别是为Hibernate或EclipseLink自动配置EntityManagerFactory设置的最便捷方法。

参考 https://docs.spring.io/spring-framework/docs/4.3.26.RELEASE/javadoc-api/org/springframework/orm/jpa/JpaVendorAdapter.html 。

JpaVendorAdapter是SPI接口,允许将特定于供应商的行为插入Spring的EntityManagerFactory创建者中。

AbstractJpaVendorAdapter类实现了JpaVendorAdapter接口,AbstractJpaVendorAdapter类的子类包括OpenJpaVendorAdapter、HibernateJpaVendorAdapter、EclipseLinkJpaVendorAdapter。

1.1.7.3. LocalContainerEntityManagerFactoryBean

参考 https://docs.spring.io/spring-framework/docs/4.3.26.RELEASE/spring-framework-reference/htmlsingle/#orm-jpa-setup-lcemfb 。

使用LocalContainerEntityManagerFactoryBean可在基于Spring的应用程序环境中提供完整的JPA功能。包括Web容器(例如Tomcat)以及独立应用程序和集成测试。

LocalContainerEntityManagerFactoryBean可以完全控制EntityManagerFactory的配置,适用于需要细粒度自定义的环境。LocalContainerEntityManagerFactoryBean创建了一个PersistenceUnitInfo实例,基于persistence.xml文件、dataSourceLookup策略和指定的loadTimeWeaver。可以使用JNDI之外的自定义数据源并控制编织过程。

LocalContainerEntityManagerFactoryBean是最强大的JPA设置选项,可以在应用程序中进行灵活的本地配置。支持链接到已存在的JDBC数据源的链接,同时支持本地和全局事务等功能。

参考 https://docs.spring.io/spring-framework/docs/4.3.26.RELEASE/javadoc-api/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.html ,LocalContainerEntityManagerFactoryBean包括以下属性:

  • dataSource

dataSource参数用于指定JPA持久性提供程序用于访问数据库的JDBC数据源。这种方法传入一个Spring管理的DataSource,是将JDBC配置保留在persistence.xml中的一种替代方法。
在JPA的角度,这里传递的数据源将用作传递给持久性提供程序的PersistenceUnitInfo的“ nonJtaDataSource”,并覆盖persistence.xml中的数据源配置(如果有)。

注意:仅在未指定外部PersistenceUnitManager的情况下适用以上指定数据源的方法。

  • jpaVendorAdapter

通过jpaVendorAdapter参数可以为所需的JPA提供程序指定JpaVendorAdapter实现(如果有)。会给定的提供程序初始化适当的默认值。

  • packagesToScan

packagesToScan参数设置对classpath中的Entity类使用基于Spring的扫描,而不是使用JPA对jar文件中带有persistence.xml中的标记的标准扫描。当使用基于Spring的扫描时,不需要persistence.xml,所需要做的就是指定要搜索的基本包。

packagesToScan参数默认为无。指定包以在classpath中搜索对Entity类的自动检测。类似于Spring的组件扫描功能(ClassPathBeanDefinitionScanner)。

注意:仅在未指定外部PersistenceUnitManager的情况下适用以上指定packagesToScan参数的方法。

  • jpaPropertyMap

指定Map形式的JPA属性,传给Persistence.createEntityManagerFactory()方法。

可在XML的bean定义中通过“map”或“props”元素填充。

jpaPropertyMap属性的设置方法包括setJpaPropertyMap()与setJpaProperties()。

可使用jpaProperties属性通过setJpaProperties()方法设置JPA属性,效果相同。setJpaProperties()方法支持在XML的bean定义中通过“props”元素填充,或通过"value"元素指定字符串格式数据填充,由PropertiesEditor解析。

1.2. JPA自动建表总结

在项目中引入Hibernate JPA实现,可以应用启动时通过Entity类自动创建对应的数据库表,可以屏蔽不同数据库的建表语句的细节差异。JPA仅用于自动创建数据库表,不通过JPA执行数据库操作。Entity类生成可以使用Java完成。

在项目中需要引入依赖javax.persistence:javax.persistence-api与org.hibernate:hibernate-core,以及lombok。

在Spring XML文件中需要定义LocalContainerEntityManagerFactoryBean对应的bean,将dataSource属性指定项目中使用的数据源,jpaVendorAdapter属性使用HibernateJpaVendorAdapter,packagesToScan属性指定Entity类所在的包,jpaProperties属性指定JPA属性,hibernate.hbm2ddl.auto应为update,以自动建表。

1.3. JPA自动建表配置

可参考示例项目中的JPA自动建表配置。

1.3.1. 添加依赖

在Gradle脚本中,添加了以下依赖:

"javax.persistence:javax.persistence-api:2.2",
"org.hibernate:hibernate-core:5.4.12.Final"

testAnnotationProcessor 'org.projectlombok:lombok:1.18.12'

1.3.2. Spring XML配置

参考示例项目src\test\resources\springJpa\springJpa.xml文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- Spring JPA配置 -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <!-- 配置JPA使用的数据源 -->
        <property name="dataSource" ref="dataSource"/>

        <!-- 配置JPA提供程序适配器,使用Hibernate -->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
        </property>

        <!-- 配置JPA Entity所在的包 -->
        <property name="packagesToScan">
            <array>
                <value>adrninistrator.test_jpa.entity</value>
                <value>adrninistrator.test_jpa.entity2</value>
            </array>
        </property>

        <!-- 配置JPA属性 -->
        <property name="jpaProperties">
            <value>
                # 自动创建或更新数据库表
                hibernate.hbm2ddl.auto=update
                # 打印SQL语句
                hibernate.show_sql=true
                hibernate.format_sql=true
            </value>
        </property>
    </bean>
</beans>

当使用JPA自动建表时,会在Spring主配置文件applicationContext.xml中添加“<import resource=“springJpa/springJpa.xml”/>”。

LocalContainerEntityManagerFactoryBean的packagesToScan属性,支持指定单个或多个Entity类所在包,其他形式的设置如下所示:

<property name="packagesToScan" value="adrninistrator.test_jpa.entity"/>
<property name="packagesToScan">
	<value>adrninistrator.test_jpa.entity, adrninistrator.test_jpa.entity2</value>
</property>

JPA属性支持通过jpaProperties、jpaPropertyMap属性进行其他形式的设置,如下所示:

<property name="jpaProperties">
	<props>
		<prop key="hibernate.hbm2ddl.auto">update</prop>
		<prop key="hibernate.show_sql">true</prop>
		<prop key="hibernate.format_sql">true</prop>
	</props>
</property>
<property name="jpaPropertyMap">
	<map>
		<entry key="hibernate.hbm2ddl.auto" value="update"/>
		<entry key="hibernate.show_sql" value="true"/>
		<entry key="hibernate.format_sql" value="true"/>
	</map>
</property>

1.3.3. H2数据库连接参数

当使用JPA自动建表时,在H2数据库的连接参数中,不再需要通过INIT参数指定创建数据库表的SQL脚本信息。

1.3.3.1. 使用H2嵌入(本地)模式

当使用H2数据库嵌入(本地)模式与JPA自动建表时,可参考示例项目unit_test_config.groovy文件中的use_h2_file_jpa元素。

在XML文件中指定的数据库连接URL参数示例如下:

jdbc:h2:file:./build/h2db;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;INIT=CREATE SCHEMA IF NOT EXISTS TEST\;SET SCHEMA TEST"

以上创建并设置SCHEMA是为了使生成的H2数据库文件能够使用数据库工具打开。

在properties文件中指定时,反斜杠需要转义为两个反斜杠“\\”;在Groovy配置文件中指定时,反斜杠需要转义为四个反斜杠“\\\\”。

1.3.3.2. 使用H2内存数据库模式

当使用H2内存数据库模式与JPA自动建表时,可参考示例项目unit_test_config.groovy文件中的use_h2_mem_jpa元素,数据库连接URL参数示例如下:

jdbc:h2:mem:;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE

1.4. JPA自动建表的时间类型

使用MySQL数据库表生成Entity类时,时间类型字段在Entity类中对应的类型使用java.sql.Timestamp。

使用JPA自动建表时,Entity类中的Timestamp类型的字段,在MySQL中创建的数据库表字段类型为datetime,不包含小数秒精度(即使MySQL原始数据库中对应字段包含);在H2中创建的数据库表字段类型为timestamp。

参考 https://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html ,MySQL的时间类型的小数秒精度默认值为0(与标准SQL的默认值6不同),即时间数据的最小精度为秒。需要注意可能导致时间数据精度与预期不相符,导致更新时返回的受影响行数改变。

参考 http://h2database.com/html/datatypes.html#timestamp_type , H2的timestamp类型的小数秒精度默认值为6,即时间数据的最小精度比秒更小。

1.5. 指定JPA自动建表的字段定义

参考 https://docs.jboss.org/hibernate/jpa/2.2/api/javax/persistence/Column.html#columnDefinition-- 。

Entity类中的@Column注解的columnDefinition属性,可以指定生成列的DDL时使用的SQL片段。

通过Entity类中的@Column注解的columnDefinition属性,可以指定创建数据库表时的对应字段的类型、非空属性、默认值、注释等字段定义,会覆盖Entity类对应字段的类型,及@Column中的length、nullable、precision、scale等属性对于字段的设置。

可参考示例项目TestTableOther类,在JPA自动建表时,创建的数据库表的字段定义与columnDefinition属性一致。

Entity类中的@Column注解的columnDefinition属性示例如下:

@Column(name = "\"flag\"", columnDefinition = "varchar(32) default 'test' COMMENT 'flag'")
private String flag;

columnDefinition属性可以准确指定数据库字段的定义,但不同数据库的语句细节可能存在差异,因此在根据数据库表生成JPA Entity类时,未使用columnDefinition属性。数据库字段的默认值只能通过columnDefinition属性指定,当需要使用数据库字段的默认值时,需要单独处理。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值