JPA 的底层实现是一些流行的开源 ORM 框架,因此JPA其实也就是java实体对象和关系型数据库建立起映射关系,通过面向对象编程的思想操作关系型数据库的规范
Getting started with Spring Data JPA
(当无心阅读英文,那就直接看代码,代码才是真正的天堂,如果自己动手运行一把,那就更加完美
其实习惯性阅读英文,会让你感觉更加靠近你梦想的技术世界)
最终目的 使用 Spring Data JPA 简化 JPA 开发
Spring集成JPA2.0
JPA的使用
进化过程- - - -丰富壮大
spring-jdbc:jdbc的原生方式开发和事务的使用
spring-jdbcTemplate:使用spring封装的JdbcTemplate开发和事务的使用
spring-hibernate-xml: hibernate的原生xml方式开发
spring-hibernate-xml-tx:hibernate的原生xml方式的事务的使用
spring-hibernate-annotation:hibernate的原生的注解方式开发
spring-hibernate-annotation-tx:hibernate的原生的注解方式的事务的使用
spring-jpa-hibernate:jpa的原生xml方式开发和事务的使用
spring-springjpa-hibernate:jpa的spring集成方式开发和事务的使用
spring-springjpa-hibernate-multDataSource:多数据源下jpa与spring集成开发和事务的使用
spring-data-jpa-hibernate:spring-data-jpa方式开发和事务的使用
<一> spring-jdbc:jdbc的原生方式开发和事务的使用
第一步: 先注册驱动类
Class.forName("com.mysql.jdbc.Driver");
第二步: 使用DriverManager来获取连接Connection
Connection conn=DriverManager.getConnection(url, user, password);
第三步: 使用Connection来执行sql语句,以及开启、回滚、提交事务
Connection conn=jdbcDao.getConnection();
conn.setAutoCommit(false);
try {
PreparedStatement ps=conn.prepareStatement("insert into user(id,name) value(?,?)");
ps.setString(1,user.getId());
ps.setInt(2,user.getName());
ps.execute();
conn.commit();
} catch (Exception e) {
e.printStackTrace();
conn.rollback();
}finally{
conn.close();
}
缺点:
每次都是获取一个连接,用完之后就释放掉,应该是用完再放回去,等待下一次使用,所以应该使用连接池datasource
<二>spring-jdbcTemplate:使用spring封装的JdbcTemplate开发和事务的使用
第一步: 首先注册dataSource,用于管理Connection,每次使用完Connection,仍旧放回连接池,等待下一次使用
<bean id="dataSource"
class="com.##.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>${jdbc.driverClass}</value>
</property>
<property name="jdbcUrl">
<value>${jdbc.url}</value>
</property>
<property name="username">
<value>${jdbc.user}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
<!--连接池中最小连接数。-->
<property name="minPoolSize">
<value>${jdbc.minPoolSize}</value>
</property>
<!--连接池中最大连接数 -->
<property name="maxPoolSize">
<value>${jdbc.maxPoolSize}</value>
</property>
<!--初始化时获取的连接数 -->
<property name="initialPoolSize">
<value>${jdbc.initialPoolSize}</value>
</property>
</bean>
第二步 然后注册jdbcTemplate,使用上述dataSource
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
第三步 事务,要注册对应的事务管理器,这里使用的是dataSource,所以要使用DataSourceTransactionManager
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
第四步 然后开启注解式的事务配置,即开启@Transactional的功能
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
第五步 Spring的@Transactional,则是使用SpringAOP来对我们的SysUserDao进行,采用的是proxy-target-class=“true” 表示我们要使用cglib来进行代理我们的Dao
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
首先我们知道jdbcTemplate原理是就是对jdbc的封装
jdbc中执行sql的Connection和执行事务功能的的Connection必须是同一个Connection
后面要实现业务代码和事务代码的分离,所以必须要确保分离之后的两者使用的是同一个Connection,否则事务是不起作用的。
hibernate的原生xml方式
第一步 hibernate.cfg.xml : 用于配置数据库连接和方言等等
<hibernate-configuration>
<session-factory name="hibernateSessionFactory">
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/</property>
<property name="hibernate.connection.username">bbl</property>
<property name="hibernate.connection.password">bbl</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.default_schema">test</property>
<mapping resource="hibernate/mapping/SysUser.hbm.xml"/>
</session-factory>
</hibernate-configuration>
第二步 SysUser.hbm.xml:用于配置实体SysUser和数据库SysUser表之间的关系
<hibernate-mapping>
<class name="com.demo.entity.SysUser" table="sysUser">
<id name="id" column="id" type="long">
<generator class="identity"/>
</id>
<property name="name" column="name" type="string"/>
</class>
</hibernate-mapping>
主要的对象
SessionFactory、Session、Transaction 这些都是hibernate的原生的对象
SessionFactory
刚才加载hibernate.cfg.xml配置文件就产生了这样的一个session工厂,它负责创建session
Session
简单理解起来就是:Session和jdbc中一个Connection差不多,Session对Connection进行了封装
Transaction
是hibernate自己的事务对象接口定义,通过session可以开启一个事务,使用如下:
Transaction tx=null;
try {
tx=session.beginTransaction();
session.save(user);
tx.commit();
} catch (Exception e) {
if(tx!=null){
tx.rollback();
}
}finally{
session.close();
}
@Repository
public class HibernateDao {
private SessionFactory sessionFactory;
public HibernateDao(){
Configuration config=new Configuration();
config.configure("hibernate/hibernate.cfg.xml");
sessionFactory=config.buildSessionFactory();
}
public Session openSession(){
return sessionFactory.openSession();
}
}
public class SysUserDao {
@Autowired
private HibernateDao hibernateDao;
public void save(SysUser user){
Session session=hibernateDao.openSession();
Transaction tx=null;
try {
tx=session.beginTransaction();
session.save(user);
tx.commit();
} catch (Exception e) {
if(tx!=null){
tx.rollback();
}
}finally{
session.close();
}
}
}
总结 : 先根据hibernate.cfg.xml配置文件来创建SessionFactory,然后我们每次就可以通过SessionFactory来获取一个Session,然后通过Session来开启一个事务Transaction,来回滚或者提交
hibernate的原生xml方式与spring集成以及事务的使用
第一步:注册dataSource
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>${jdbc.driverClass}</value>
</property>
<property name="jdbcUrl">
<value>${jdbc.url}</value>
</property>
<property name="user">
<value>${jdbc.user}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
<!--连接池中最小连接数。-->
<property name="minPoolSize">
<value>${jdbc.minPoolSize}</value>
</property>
<!--连接池中最大连接数。 -->
<property name="maxPoolSize">
<value>${jdbc.maxPoolSize}</value>
</property>
<!--初始化时获取的连接数 -->
<property name="initialPoolSize">
<value>${jdbc.initialPoolSize}</value>
</property>
</bean>
第二步:使用spring的LocalSessionFactoryBean来创建SessionFactory
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>hibernate/mapping/SysUser.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=true
</value>
</property>
</bean>
第三步:注册hibernate的事务管理器 HibernateTransactionManager
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
第四步:启用spring的@Transactional的注解支持
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
使用
@Repository
public class UserDao {
@Autowired
private SessionFactory sessionFactory;
@Transactional
public void save(SysUser user){
//Session session=sessionFactory.openSession();
Session session=sessionFactory.getCurrentSession();
session.save(user);
throw new RuntimeException();
}
}
总结
在执行save方法之前,使用了HibernateTransactionManager开启了相应的事务
先使用sessionFactory的openSession()来创建一个session
使用session从dataSource中获取了一个connection
使用session的getTransaction方法开启了一个Hibernate的一个Transaction事务
session.save(user);
使用session来持久化user
hibernate的注解方式开发与Spring集成和事务的使用
xml方式:
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="mappingResources">
<list>
<value>hibernate/mapping/SysUser .hbm.xml</value>
</list>
</property>
//其他略
</bean>
注解方式:
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="packagesToScan">
<list>
<value>com.demo.entity</value>
</list>
</property>
//其他略
</bean>
jpa原生开发和事务的使用
jpa仅仅是一层接口规范
第一步:就是jpa的核心配置文件 persistence.xml
默认情况下该配置文件存放的位置是classpath路径下 META-INF/persistence.xml。配置文件的内容如下:
<persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/test" />
<property name="hibernate.connection.username" value="root" />
<property name="hibernate.connection.password" value="ligang" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="update" />
</properties>
</persistence-unit>
第二步: 根据核心配置文件创建EntityManagerFactory
EntityManagerFactory entityManagerFactory=Persistence.createEntityManagerFactory("test");
第三步:根据EntityManagerFactory创建出EntityManager
EntityManager entityManager=entityManagerFactory.createEntityManager();
第四步 : 根据EntityManager对实体entity进行增删改查
entityManager.save(user);
说明
PersistenceProvider接口:
根据持久化单元名称和配置参数创建出EntityManagerFactory,接口定义如下:
public interface PersistenceProvider {
public EntityManagerFactory createEntityManagerFactory(String emName, Map map);
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map);
}
jpa仅仅是一层接口规范,不同的底层的实现者提供各自的provider。如hibernate提供的provider实现是org.hibernate.jpa.HibernatePersistenceProvider
EntityManagerFactory接口:
就是EntityManager的工厂。其实可以类似于Hibernate中的SessionFactory。对于Hibernate来说,其实它就是对SessionFactory的封装,Hibernate实现的EntityManagerFactory是EntityManagerFactoryImpl,源码如下:
public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory {
private final transient SessionFactoryImpl sessionFactory;
//略
}
EntityManager接口:
能够对实体entity进行增删改查,其实可以类似于Hibernate中的Session。对于Hibernate来说,其实它就是对Session的封装。Hibernate实现的EntityManager是EntityManagerImpl,源码如下:
public class EntityManagerImpl extends AbstractEntityManagerImpl implements SessionOwner {
protected Session session;
//略
}
EntityTransaction接口:
jpa定义的事务接口,其实可以类似于Hibernate原生的的Transaction接口。对于Hibernate来说,其实它就是对Transaction的封装。Hibernate实现的EntityTransaction是TransactionImpl,源码如下:
public class TransactionImpl implements EntityTransaction {
private Transaction tx;
//略
}
使用
@Repository
public class JpaTestDao {
private EntityManagerFactory entityManagerFactory;
public JpaTestDao (){
entityManagerFactory=Persistence.createEntityManagerFactory("test");
}
public EntityManager getEntityManager(){
return entityManagerFactory.createEntityManager();
}
}
@Repository
public class SysUserDao {
@Autowired
private JpaTestDao TestDao;
public void save(SysUseruser){
EntityManager entityManager=TestDao.getEntityManager();
EntityTransaction tx=null;
try {
tx=entityManager.getTransaction();
tx.begin();
entityManager.persist(user);
tx.commit();
} catch (Exception e) {
if(tx!=null){
tx.rollback();
}
}finally{
entityManager.close();
}
}
}
jpa与spring的集成
第一步:配置数据库连接池dataSource
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>${jdbc.driverClass}</value>
</property>
<property name="jdbcUrl">
<value>${jdbc.url}</value>
</property>
<property name="user">
<value>${jdbc.user}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
<!--连接池中最小连接数。-->
<property name="minPoolSize">
<value>${jdbc.minPoolSize}</value>
</property>
<!--连接池中最大连接数。-->
<property name="maxPoolSize">
<value>${jdbc.maxPoolSize}</value>
</property>
<!--初始化时获取的连接数-->
<property name="initialPoolSize">
<value>${jdbc.initialPoolSize}</value>
</property>
</bean>
第二步:配置entityManagerFactory
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="MYSQL"/>
<property name="showSql" value="true"/>
<property name="generateDdl" value="true"/>
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<property name="packagesToScan" value="com.demo.entity"/>
</bean>
第三步:配置事务管理器JpaTransactionManager
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
第四步:启动@Transactional注解的处理器
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
使用
@Repository
public class SysUserDao {
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void save(SysUser user){
entityManager.persist(user);
}
}
总结
@PersistenceContext注解来获取entityManagerFactory创建的EntityManager对象,然后使用EntityManager进行增删改查
如何创建EntityManagerFactory :
可以看到spring是使用了一个工厂bean LocalContainerEntityManagerFactoryBean来创建entityManagerFactory。虽然配置的是一个工厂bean,但是容器在根据id来获取bean的时候,返回的是该工厂bean所创建的实体,即LocalContainerEntityManagerFactoryBean所创建的EntityManagerFactory。
方式1:当LocalEntityManagerFactoryBean本身指定了PersistenceProvider,就使用该PersistenceProvider来创建EntityManagerFactory,详见上文的PersistenceProvider接口定义
public class LocalEntityManagerFactoryBean extends AbstractEntityManagerFactoryBean {
@Override
protected EntityManagerFactory createNativeEntityManagerFactory()
throws PersistenceException {
PersistenceProvider provider = getPersistenceProvider();
if (provider != null) {
// Create EntityManagerFactory directly through PersistenceProvider.
EntityManagerFactory emf = provider.createEntityManagerFactory
(getPersistenceUnitName(), getJpaPropertyMap());
return emf;
}
else {
// Let JPA perform its standard PersistenceProvider autodetection.
return Persistence.createEntityManagerFactory(
getPersistenceUnitName(), getJpaPropertyMap());
}
}
//略了部分内容
}
方式2:使用上文jpa原生方式的:
EntityManagerFactory entityManagerFactory=Persistence.createEntityManagerFactory("test");
它其实也是先获取所有的PersistenceProvider,然后遍历PersistenceProvider来创建EntityManagerFactory,源码如下:
public static EntityManagerFactory createEntityManagerFactory(String
persistenceUnitName, Map properties) {
EntityManagerFactory emf = null;
List<PersistenceProvider> providers = getProviders();
for ( PersistenceProvider provider : providers ) {
emf = provider.createEntityManagerFactory( persistenceUnitName, properties );
if ( emf != null ) {
break;
}
}
if ( emf == null ) {
throw new PersistenceException( "No Persistence provider
for EntityManager named " + persistenceUnitName );
}
return emf;
}
多数据源下jpa与spring集成开发和事务的使用
第一步 配置两个数据库连接池dataSource
<bean id="dataSource1"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>${jdbc1.driverClass}</value>
</property>
<property name="jdbcUrl">
<value>${jdbc1.url}</value>
</property>
//略,见项目中的配置
</bean>
<!-- 配置数据源 -->
<bean id="dataSource2"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>${jdbc2.driverClass}</value>
</property>
<property name="jdbcUrl">
<value>${jdbc2.url}</value>
</property>
//略,见项目中的配置
</bean>
第二步 配置2个entityManagerFactory
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="MYSQL"/>
<property name="showSql" value="true"/>
<property name="generateDdl" value="true"/>
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
</bean>
<bean id="entityManagerFactory1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource1"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<property name="packagesToScan" value="com.demo.entity"/>
</bean>
<bean id="entityManagerFactory2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource2"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<property name="packagesToScan" value="com.demo.entity"/>
<property name="persistenceUnitName" value="test2"/>
</bean>
这两个entityManagerFactory使用不同的dataSource,各自有一个persistenceUnitName名字(持久化单元的名字),分别叫"test1"和"test2”。在上文中,并没有配置persistenceUnitName,采用默认值"default”
第三步 配置2个事务管理器
<bean id="transactionManager1" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory1"></property>
</bean>
<bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory2"></property>
</bean>
第四步 开启@Transactional的处理器
<tx:annotation-driven proxy-target-class="true"/>
使用
@Repository
public class SysUserDao {
@PersistenceContext(unitName="test1")
private EntityManager entityManager;
@PersistenceContext(unitName="test2")
private EntityManager entityManagerTest2;
@Transactional("transactionManager1")
public void save(SysUser user){
entityManager.persist(user);
}
@Transactional("transactionManager2")
public void save2(SysUseruser){
entityManagerTest2.persist(user);
}
}
spring-data-jpa的开发和事务的使用
第一步 配置dataSource
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>${jdbc.driverClass}</value>
</property>
<property name="jdbcUrl">
<value>${jdbc.url}</value>
</property>
//略
</bean>
第二步 配置entityManagerFactory
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="oracle/mysql"/>
<property name="showSql" value="true"/>
<property name="generateDdl" value="true"/>
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<property name="packagesToScan" value="com.demo.entity"/>
</bean>
第三步 配置transactionManager
第四步 开启@Transactional的处理器
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean> 第三步
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/> 第四步
第五步 配置要扫描的dao的路径
<jpa:repositories base-package="com.sys.dao"/>
使用
public interface SysUserDao extends CrudRepository<SysUser,Long>{
}
@Autowired
private UserDao userDao;