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'
// hibernate
implementation 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
*/
@Bean
public 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
*/
@Bean
public 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
*/
@Bean
public HibernateTransactionManager hibernateTransactionManager() {
return new HibernateTransactionManager(sessionFactory().getObject());
}
/**
* 声明 dao
* @return
*/
@Bean
public EventDao eventDao() {
final EventDao eventDao = new EventDao();
eventDao.setSessionFactory(this.sessionFactory().getObject());
return eventDao;
}
/**
* 声明服务类
* @return
*/
@Bean
public 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 events
this.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 {
@Autowired
private EventDao eventDao;
@Transactional
public 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 {
@Autowired
private SessionFactory sessionFactory;
@Autowired
private Service service;
@Autowired
private EventDao eventDao;
@Test
public void save() {
// 这个例子不会报错,因为在 save 方法上使用了事务注解 @Transactional
final Event event = service.save("李四");
System.out.println(event);
}
@Test
public void tx() {
// 这个例子会报错:Could not obtain transaction-synchronized Session for current thread
// 因为 sessionFactory.getCurrentSession() 需要在事务中运行
final Collection x = eventDao.list("x");
System.out.println(x);
}
/**
* 使用原始的 session 手动开启事务
*/
@Test
public 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();
}
}