SessionFactory Setup in a Spring Container
为了避免将应用程序对象与硬编码的资源查找联系起来,你可以在 Spring 容器中把资源(如 JDBC DataSource 或 Hibernate SessionFactory)定义为 Bean。需要访问资源的应用程序对象通过 Bean 引用接收对这种预定义实例的引用,正如下一节中的 DAO 定义所说明的那样。
下面是 XML 应用上下文定义的摘录,显示了如何在上面设置 JDBC DataSource 和 Hibernate SessionFactory:
<beans><bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><property name="driverClassName" value="org.hsqldb.jdbcDriver"/><property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/><property name="username" value="sa"/><property name="password" value=""/></bean><bean id="mySessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"><property name="dataSource" ref="myDataSource"/><property name="mappingResources"><list><value>product.hbm.xml</value></list></property><property name="hibernateProperties"><value>hibernate.dialect=org.hibernate.dialect.HSQLDialect</value></property></bean></beans>
从本地的 Jakarta Commons DBCP BasicDataSource 切换到 JNDI 定位的 DataSource(通常由应用服务器管理)只是一个配置问题,如下例所示:
<beans><jee:jndi-lookup id="myDataSource" jndi-name="java:comp/env/jdbc/myds"/></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。 :::
这是一个有事务的例子项目
如果看不懂,先看完 声明性事务 这一章后,再来看这个项目示例
添加依赖
dependencies {testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.8.2'implementation group: 'org.springframework', name: 'spring-test', version: '5.3.15'implementation group: 'org.springframework', name: 'spring-core', version: '5.3.15'implementation group: 'org.springframework', name: 'spring-context', version: '5.3.15'implementation group: 'org.springframework', name: 'spring-aop', version: '5.3.15'implementation group: 'org.springframework', name: 'spring-jdbc', version: '5.3.15'implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.28'// hibernateimplementation group: 'org.springframework', name: 'spring-orm', version: '5.3.15'implementation group: 'org.hibernate', name: 'hibernate-core', version: '5.4.33.Final'}
配置
package cn.mrcode.study.springdocsread;import com.mysql.cj.jdbc.MysqlDataSource;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.ClassPathResource;import org.springframework.orm.hibernate5.HibernateTransactionManager;import org.springframework.orm.hibernate5.LocalSessionFactoryBean;import org.springframework.transaction.annotation.EnableTransactionManagement;import java.util.Properties;import javax.sql.DataSource;import cn.mrcode.study.springdocsread.dao.EventDao;import cn.mrcode.study.springdocsread.service.Service;/*** @author mrcode*/@Configuration@EnableTransactionManagement // 开启事物管理器public class AppConfig {/*** 声明数据源* @return*/@Beanpublic DataSource dataSource() {final MysqlDataSource dataSource = new MysqlDataSource();dataSource.setURL("jdbc:mysql://127.0.0.1:3306/spring-read-docs?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=true");dataSource.setUser("root");dataSource.setPassword("root");return dataSource;}/*** sessionFactoryBean* @return*/@Beanpublic LocalSessionFactoryBean sessionFactory() {final LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();sessionFactoryBean.setDataSource(dataSource());sessionFactoryBean.setMappingLocations(new ClassPathResource("/hbm/Event.hbm.xml"));final Properties hibernateProperties = new Properties();hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");sessionFactoryBean.setHibernateProperties(hibernateProperties);return sessionFactoryBean;}/*** 配置事务管理器* @return*/@Beanpublic HibernateTransactionManager hibernateTransactionManager() {return new HibernateTransactionManager(sessionFactory().getObject());}/*** 声明 dao* @return*/@Beanpublic EventDao eventDao() {final EventDao eventDao = new EventDao();eventDao.setSessionFactory(this.sessionFactory().getObject());return eventDao;}/*** 声明服务类* @return*/@Beanpublic Service service() {return new Service();}}
实体类
package cn.mrcode.study.springdocsread.dao;import java.util.Date;public class Event {private Long id;private String title;private Date date;public Event() {// this form used by Hibernate}public Event(String title, Date date) {// for application use, to create new eventsthis.title = title;this.date = date;}public Long getId() {return id;}private void setId(Long id) {this.id = id;}public Date getDate() {return date;}public void setDate(Date date) {this.date = date;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}}
dao 类
package cn.mrcode.study.springdocsread.dao;import org.hibernate.SessionFactory;import java.util.Collection;import java.util.Date;/*** @author mrcode*/public class EventDao {private SessionFactory sessionFactory;public void setSessionFactory(SessionFactory sessionFactory) {this.sessionFactory = sessionFactory;}public Collection list(String category) {return this.sessionFactory.getCurrentSession().createQuery("from Event")// .setParameter(0, category).list();}public Event save(String name) {final Event event = new Event();event.setTitle(name);event.setDate(new Date());this.sessionFactory.getCurrentSession().save(event);return event;}}
服务类
package cn.mrcode.study.springdocsread.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.transaction.annotation.Transactional;import cn.mrcode.study.springdocsread.dao.Event;import cn.mrcode.study.springdocsread.dao.EventDao;/*** @author mrcode*/public class Service {@Autowiredprivate EventDao eventDao;@Transactionalpublic Event save(String name) {return eventDao.save(name);}}
hbm/Event.hbm.xml
<?xml version="1.0"?><!--~ Hibernate, Relational Persistence for Idiomatic Java~~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.--><!DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="cn.mrcode.study.springdocsread.dao"><class name="Event" table="event"><id name="id" column="event_id"><generator class="increment"/></id><property name="date" type="timestamp" column="event_date"/><property name="title"/></class></hibernate-mapping>
创建表的语句
CREATE TABLE `event` (`event_id` int(11) NOT NULL AUTO_INCREMENT,`event_date` timestamp NULL DEFAULT NULL,`title` varchar(255) DEFAULT NULL,PRIMARY KEY (`event_id`)) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
测试用例
package cn.mrcode.study.springdocsread.test;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;import java.util.Collection;import java.util.Date;import java.util.List;import cn.mrcode.study.springdocsread.AppConfig;import cn.mrcode.study.springdocsread.dao.Event;import cn.mrcode.study.springdocsread.dao.EventDao;import cn.mrcode.study.springdocsread.service.Service;/*** @author mrcode*/@SpringJUnitConfig({AppConfig.class})public class DemoTest {@Autowiredprivate SessionFactory sessionFactory;@Autowiredprivate Service service;@Autowiredprivate EventDao eventDao;@Testpublic void save() {// 这个例子不会报错,因为在 save 方法上使用了事务注解 @Transactionalfinal Event event = service.save("李四");System.out.println(event);}@Testpublic void tx() {// 这个例子会报错:Could not obtain transaction-synchronized Session for current thread// 因为 sessionFactory.getCurrentSession() 需要在事务中运行final Collection x = eventDao.list("x");System.out.println(x);}/*** 使用原始的 session 手动开启事务*/@Testpublic void nat() {Session session = sessionFactory.openSession();session.beginTransaction();final Event event = new Event();event.setDate(new Date());event.setTitle("张三");session.save(event);session.getTransaction().commit();session.close();System.out.println(event.getId());session = sessionFactory.openSession();final List from_event = session.createQuery("from Event").list();System.out.println(sessionFactory);session.close();}}
