SessionFactory Setup in a Spring Container

为了避免将应用程序对象与硬编码的资源查找联系起来,你可以在 Spring 容器中把资源(如 JDBC DataSource 或 Hibernate SessionFactory)定义为 Bean。需要访问资源的应用程序对象通过 Bean 引用接收对这种预定义实例的引用,正如下一节中的 DAO 定义所说明的那样。

下面是 XML 应用上下文定义的摘录,显示了如何在上面设置 JDBC DataSource 和 Hibernate SessionFactory:

  1. <beans>
  2. <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  3. <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
  4. <property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
  5. <property name="username" value="sa"/>
  6. <property name="password" value=""/>
  7. </bean>
  8. <bean id="mySessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
  9. <property name="dataSource" ref="myDataSource"/>
  10. <property name="mappingResources">
  11. <list>
  12. <value>product.hbm.xml</value>
  13. </list>
  14. </property>
  15. <property name="hibernateProperties">
  16. <value>
  17. hibernate.dialect=org.hibernate.dialect.HSQLDialect
  18. </value>
  19. </property>
  20. </bean>
  21. </beans>

从本地的 Jakarta Commons DBCP BasicDataSource 切换到 JNDI 定位的 DataSource(通常由应用服务器管理)只是一个配置问题,如下例所示:

  1. <beans>
  2. <jee:jndi-lookup id="myDataSource" jndi-name="java:comp/env/jdbc/myds"/>
  3. </beans>

你也可以访问 JNDI 定位的 SessionFactory,使用 Spring 的 JndiObjectFactoryBean / <jee:jndi-lookup>来检索和公开它。然而,这在 EJB 上下文之外通常并不常见。

:::info Spring 还提供了一个 LocalSessionFactoryBuilder 的变体,与 @Bean 风格的配置和编程设置无缝集成(不涉及 FactoryBean)。

LocalSessionFactoryBean 和 LocalSessionFactoryBuilder 都支持后台引导,Hibernate 的初始化与特定引导执行器(如SimpleAsyncTaskExecutor)上的应用引导线程并行运行。在 LocalSessionFactoryBean 上,这可以通过 bootstrapExecutor 属性获得。在可编程的 LocalSessionFactoryBuilder 上,有一个重载的 buildSessionFactory 方法,它需要一个 bootstrap 执行器参数。

从 Spring Framework 5.1 开始,这样的本地 Hibernate 设置也可以暴露 JPA EntityManagerFactory,以便在本地 Hibernate 访问之外进行标准的 JPA 交互。详见 Native Hibernate Setup for JPA。 :::

这是一个有事务的例子项目

如果看不懂,先看完 声明性事务 这一章后,再来看这个项目示例

添加依赖

  1. dependencies {
  2. testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.8.2'
  3. implementation group: 'org.springframework', name: 'spring-test', version: '5.3.15'
  4. implementation group: 'org.springframework', name: 'spring-core', version: '5.3.15'
  5. implementation group: 'org.springframework', name: 'spring-context', version: '5.3.15'
  6. implementation group: 'org.springframework', name: 'spring-aop', version: '5.3.15'
  7. implementation group: 'org.springframework', name: 'spring-jdbc', version: '5.3.15'
  8. implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.28'
  9. // hibernate
  10. implementation group: 'org.springframework', name: 'spring-orm', version: '5.3.15'
  11. implementation group: 'org.hibernate', name: 'hibernate-core', version: '5.4.33.Final'
  12. }

配置

  1. package cn.mrcode.study.springdocsread;
  2. import com.mysql.cj.jdbc.MysqlDataSource;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.core.io.ClassPathResource;
  6. import org.springframework.orm.hibernate5.HibernateTransactionManager;
  7. import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
  8. import org.springframework.transaction.annotation.EnableTransactionManagement;
  9. import java.util.Properties;
  10. import javax.sql.DataSource;
  11. import cn.mrcode.study.springdocsread.dao.EventDao;
  12. import cn.mrcode.study.springdocsread.service.Service;
  13. /**
  14. * @author mrcode
  15. */
  16. @Configuration
  17. @EnableTransactionManagement // 开启事物管理器
  18. public class AppConfig {
  19. /**
  20. * 声明数据源
  21. * @return
  22. */
  23. @Bean
  24. public DataSource dataSource() {
  25. final MysqlDataSource dataSource = new MysqlDataSource();
  26. dataSource.setURL("jdbc:mysql://127.0.0.1:3306/spring-read-docs?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=true");
  27. dataSource.setUser("root");
  28. dataSource.setPassword("root");
  29. return dataSource;
  30. }
  31. /**
  32. * sessionFactoryBean
  33. * @return
  34. */
  35. @Bean
  36. public LocalSessionFactoryBean sessionFactory() {
  37. final LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
  38. sessionFactoryBean.setDataSource(dataSource());
  39. sessionFactoryBean.setMappingLocations(new ClassPathResource("/hbm/Event.hbm.xml"));
  40. final Properties hibernateProperties = new Properties();
  41. hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
  42. sessionFactoryBean.setHibernateProperties(hibernateProperties);
  43. return sessionFactoryBean;
  44. }
  45. /**
  46. * 配置事务管理器
  47. * @return
  48. */
  49. @Bean
  50. public HibernateTransactionManager hibernateTransactionManager() {
  51. return new HibernateTransactionManager(sessionFactory().getObject());
  52. }
  53. /**
  54. * 声明 dao
  55. * @return
  56. */
  57. @Bean
  58. public EventDao eventDao() {
  59. final EventDao eventDao = new EventDao();
  60. eventDao.setSessionFactory(this.sessionFactory().getObject());
  61. return eventDao;
  62. }
  63. /**
  64. * 声明服务类
  65. * @return
  66. */
  67. @Bean
  68. public Service service() {
  69. return new Service();
  70. }
  71. }

实体类

  1. package cn.mrcode.study.springdocsread.dao;
  2. import java.util.Date;
  3. public class Event {
  4. private Long id;
  5. private String title;
  6. private Date date;
  7. public Event() {
  8. // this form used by Hibernate
  9. }
  10. public Event(String title, Date date) {
  11. // for application use, to create new events
  12. this.title = title;
  13. this.date = date;
  14. }
  15. public Long getId() {
  16. return id;
  17. }
  18. private void setId(Long id) {
  19. this.id = id;
  20. }
  21. public Date getDate() {
  22. return date;
  23. }
  24. public void setDate(Date date) {
  25. this.date = date;
  26. }
  27. public String getTitle() {
  28. return title;
  29. }
  30. public void setTitle(String title) {
  31. this.title = title;
  32. }
  33. }

dao 类

  1. package cn.mrcode.study.springdocsread.dao;
  2. import org.hibernate.SessionFactory;
  3. import java.util.Collection;
  4. import java.util.Date;
  5. /**
  6. * @author mrcode
  7. */
  8. public class EventDao {
  9. private SessionFactory sessionFactory;
  10. public void setSessionFactory(SessionFactory sessionFactory) {
  11. this.sessionFactory = sessionFactory;
  12. }
  13. public Collection list(String category) {
  14. return this.sessionFactory.getCurrentSession()
  15. .createQuery("from Event")
  16. // .setParameter(0, category)
  17. .list();
  18. }
  19. public Event save(String name) {
  20. final Event event = new Event();
  21. event.setTitle(name);
  22. event.setDate(new Date());
  23. this.sessionFactory.getCurrentSession().save(event);
  24. return event;
  25. }
  26. }

服务类

  1. package cn.mrcode.study.springdocsread.service;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.transaction.annotation.Transactional;
  4. import cn.mrcode.study.springdocsread.dao.Event;
  5. import cn.mrcode.study.springdocsread.dao.EventDao;
  6. /**
  7. * @author mrcode
  8. */
  9. public class Service {
  10. @Autowired
  11. private EventDao eventDao;
  12. @Transactional
  13. public Event save(String name) {
  14. return eventDao.save(name);
  15. }
  16. }

hbm/Event.hbm.xml

  1. <?xml version="1.0"?>
  2. <!--
  3. ~ Hibernate, Relational Persistence for Idiomatic Java
  4. ~
  5. ~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
  6. ~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
  7. -->
  8. <!DOCTYPE hibernate-mapping PUBLIC
  9. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  10. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
  11. <hibernate-mapping package="cn.mrcode.study.springdocsread.dao">
  12. <class name="Event" table="event">
  13. <id name="id" column="event_id">
  14. <generator class="increment"/>
  15. </id>
  16. <property name="date" type="timestamp" column="event_date"/>
  17. <property name="title"/>
  18. </class>
  19. </hibernate-mapping>

创建表的语句

  1. CREATE TABLE `event` (
  2. `event_id` int(11) NOT NULL AUTO_INCREMENT,
  3. `event_date` timestamp NULL DEFAULT NULL,
  4. `title` varchar(255) DEFAULT NULL,
  5. PRIMARY KEY (`event_id`)
  6. ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

测试用例

  1. package cn.mrcode.study.springdocsread.test;
  2. import org.hibernate.Session;
  3. import org.hibernate.SessionFactory;
  4. import org.junit.jupiter.api.Test;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
  7. import java.util.Collection;
  8. import java.util.Date;
  9. import java.util.List;
  10. import cn.mrcode.study.springdocsread.AppConfig;
  11. import cn.mrcode.study.springdocsread.dao.Event;
  12. import cn.mrcode.study.springdocsread.dao.EventDao;
  13. import cn.mrcode.study.springdocsread.service.Service;
  14. /**
  15. * @author mrcode
  16. */
  17. @SpringJUnitConfig({AppConfig.class})
  18. public class DemoTest {
  19. @Autowired
  20. private SessionFactory sessionFactory;
  21. @Autowired
  22. private Service service;
  23. @Autowired
  24. private EventDao eventDao;
  25. @Test
  26. public void save() {
  27. // 这个例子不会报错,因为在 save 方法上使用了事务注解 @Transactional
  28. final Event event = service.save("李四");
  29. System.out.println(event);
  30. }
  31. @Test
  32. public void tx() {
  33. // 这个例子会报错:Could not obtain transaction-synchronized Session for current thread
  34. // 因为 sessionFactory.getCurrentSession() 需要在事务中运行
  35. final Collection x = eventDao.list("x");
  36. System.out.println(x);
  37. }
  38. /**
  39. * 使用原始的 session 手动开启事务
  40. */
  41. @Test
  42. public void nat() {
  43. Session session = sessionFactory.openSession();
  44. session.beginTransaction();
  45. final Event event = new Event();
  46. event.setDate(new Date());
  47. event.setTitle("张三");
  48. session.save(event);
  49. session.getTransaction().commit();
  50. session.close();
  51. System.out.println(event.getId());
  52. session = sessionFactory.openSession();
  53. final List from_event = session.createQuery("from Event").list();
  54. System.out.println(sessionFactory);
  55. session.close();
  56. }
  57. }