本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Hibernate 遇上 Spring
当我们想要在 Spring 项目中使用 Hibernate 时,可以考虑 Spring 的 Spring Data Jpa 扩展。
Spring Data Jpa 基于 Hibernate 访问数据库,是简化 JPA 开发的框架,我们只需要按照约定写 DAO 层即可通过方法名称约定生成 JPA 查询,最终以 Spring 的方式来使用 JPA。
Spring Data Jpa 也包括一些额外的功能:分页、排序、复杂查询等。
项目依赖
<!-- Spring IoC 支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- Spring AOP 支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<!-- Spring 数据库连接池支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<!-- Spring 对 Hibernate 的支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<!-- Spring 对 Jpa 的支持 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- hibernate orm -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- postgresql 数据库驱动 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.5.0</version>
</dependency>
测试 Spring Data Jpa
public interface ProductDao extends JpaRepository<Product, Integer>, JpaSpecificationExecutor<Product> {
}
@Entity
@Table
@Data
public class Product implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private Integer productId;
@Column
private String productName;
}
c3p0 数据库连接池配置:
c3p0.minPoolSize=5
c3p0.maxPoolSize=20
c3p0.idleConnectionTestPeriod=60
c3p0.maxIdleTime=120
c3p0.acquireIncrement=2
c3p0.initialPoolSize=10
c3p0.jdbcUrl=jdbc:postgresql://127.0.0.1:5432/smbms
c3p0.driverClass=org.postgresql.Driver
c3p0.user=postgres
c3p0.password=123456
Spring 配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc https://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa https://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--c3p0的数据源-->
<context:property-placeholder location="classpath:c3p0.properties"/>
<!--Spring整合hibernate的第二种方式:定义c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close" >
<property name="user" value="${c3p0.user}"/>
<property name="password" value="${c3p0.password}"/>
<property name="driverClass" value="${c3p0.driverClass}"/>
<property name="jdbcUrl" value="${c3p0.jdbcUrl}"/>
<!--minPoolSize :最小连接数(default 3)-->
<property name="minPoolSize" value="${c3p0.minPoolSize}"/>
<!--maxPoolSize :最大连接数(default 15)-->
<property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>
<!--initialPoolSize : 初始连接数(default 3)-->
<property name="initialPoolSize" value="${c3p0.initialPoolSize}"/>
<!--maxIdleTime :最大闲置时间, 未使用的连接在被丢弃之前在连接池存活时间,单位为秒(default 0, 永久存活)-->
<property name="maxIdleTime" value="${c3p0.maxIdleTime}"/>
<!--acquireIncrement : 获取连接数量, 连接不足时,c3p0尝试一次获取新连接的个数(default 3)-->
<property name="acquireIncrement" value="${c3p0.acquireIncrement}"/>
<!-- 空闲检查时间间隔, 每隔60秒检查连接池里的空闲连接 ,单位是秒-->
<property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="cn.smbms.entity" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="POSTGRESQL" />
<property name="generateDdl" value="false" />
<property name="databasePlatform" value="org.hibernate.dialect.PostgreSQL10Dialect"/>
<property name="showSql" value="true"/>
</bean>
</property>
</bean>
<jpa:repositories base-package="cn.smbms.dao" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!--扫描注解-->
<context:component-scan base-package="cn.smbms"/>
</beans>
启动 Spring 应用进行测试:
public class TestProductDao {
@Test
public void test_01(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
ProductDao productDao = applicationContext.getBean(ProductDao.class);
List<Product> all = productDao.findAll();
all.forEach(System.out::println);
}
}
Spring Data Jpa 关键配置说明
transactionManager、entityManagerFactory、jpaVendorAdapter、dataSource。
- com.mchange.v2.c3p0.ComboPooledDataSource:基于C3P0的数据库连接池,对javax.sql.DataSource的实现,用于管理 Spring 应用中数据库连接。
- org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter:适配数据库。
- org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean:
- 是一个FactoryBean,用于构造 javax.persistence.EntityManagerFactory Bean, 参考 Java 直接使用 Hibernate 用法。
- 基于 Spring 的扫描,不需要 persistence.xml 扫描 entity。
- 依赖 jpaVendorAdapter 和 dataSource。
- org.springframework.orm.jpa.JpaTransactionManager:jpa的事务管理器。
- <jpa:repositories base-package="cn.smbms.dao" />:扫描并动态代理继承了 JpaRepository 的 dao,并将代理后的 dao 动态注册到 IoC 容器。
关于对 JpaRepository 接口的代理
用户自定义 JpaRepository dao 层接口在容器中的bean 实际上统一映射为一个 JpaRepositoryFactoryBean 对象,使用者对这样一个 bean 进行依赖注入时,会调用其 FactoryBean#getObject 获取真正要注入的 SimpleJpaRepository 代理对象。
RepositoryFactoryBean 中依赖注入了 JPA entityManager 实例,也就能操作数据库。最终生成的用户自定义接口 JpaRepository 依赖于 JPA 的实现 Hibernate,Spring Data Jpa 的操作数据库的 SQL 的生成和执行也会依靠 Hibernate 来完成。
关于对 jpa:repositories 标签的解析
主要就是启用了 spring.handlers 对 <jpa:repositories base-package="cn.smbms.dao" /> 标签进行解析。spring.handlers 文件配置告诉 Spring 该如何来解析你自定义的配置文件。
在这个配置类中,通过 registerBeanDefinitionParser 向 Spring 注册了 RepositoryBeanDefinitionParser 和 AuditingBeanDefinitionParser 与 JPA 有关的 BeanDefinition 解析器。