带有注释和(动态)AbstractRoutingDataSource 的 Spring 3.1.3 + Hibernate 配置
Posted
技术标签:
【中文标题】带有注释和(动态)AbstractRoutingDataSource 的 Spring 3.1.3 + Hibernate 配置【英文标题】:Spring 3.1.3 + Hibernate Configuration with annotations and with (Dynamic) AbstractRoutingDataSource 【发布时间】:2015-04-30 05:26:14 【问题描述】:我正在尝试仅使用一个数据源更改注释休眠状态,以使尽可能多的数据源保存在数据库中。为了使用户拥有分配的连接,并添加新类型的连接,只需重新启动服务器(避免 .war 重新编译)
服务器首先加载一个 SecurityHibernateConfiguration 没有任何问题:
@Configuration
@EnableTransactionManagement
public class SecurityHibernateConfiguration
@Autowired
public Parameters parameters;
@Bean
DataSource datasourcesecurity()
org.apache.commons.dbcp.BasicDataSource dataSource = new org.apache.commons.dbcp.BasicDataSource();
dataSource.setDriverClassName(parameters.getDriverClassName());
dataSource.setUrl(parameters.getUrlSecurity());
dataSource.setUsername(parameters.getUserNameSecurity());
dataSource.setPassword(parameters.getPasswordSecurity());
return dataSource;
@Bean
public SessionFactory securitySessionFactory() throws Exception
Properties props = new Properties();
props.put("hibernate.dialect", parameters.getHibernateDialect());
props.put("hibernate.format_sql", parameters.getFormatSql());
AnnotationSessionFactoryBean bean = new AnnotationSessionFactoryBean();
bean.setAnnotatedClasses(new Class[]
Login.class,
LoginRol.class,
Aplicacio.class,
Rol.class,
RolObjecte.class,
Objecte.class,
RolObjecteAcl.class,
Acl.class,
Tema.class,
Connexio.class
);
bean.setHibernateProperties(props);
bean.setDataSource(datasourcesecurity());
bean.setSchemaUpdate(false);
bean.afterPropertiesSet();
SessionFactory factory = bean.getObject();
return factory;
@Bean
public HibernateTransactionManager securitytransactionManager() throws Exception
return new HibernateTransactionManager(securitySessionFactory());
然后我创建了一个这样的路由数据源:
public class RoutingDataSource extends AbstractRoutingDataSource
@Autowired
private SecurityManager securitymanager;
private Map<Long, DataSource> targetDataSources = new HashMap<Long, DataSource>();
@SuppressWarnings( "unchecked", "rawtypes" )
public void setTargetDataSources(Map targetDataSources)
this.targetDataSources = (Map<Long, DataSource>) targetDataSources;
@Override
protected DataSource determineTargetDataSource()
Long lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.targetDataSources.get(lookupKey);
if (dataSource == null)
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
return dataSource;
@Override
protected Long determineCurrentLookupKey()
try
String username = securitymanager.getUserName();
Login login = null;
if (!StringUtils.isEmpty(username))
login = securitymanager.getLogin(username);
return login == null ? 1L : login.getConnexio() == null ? 1L : login.getConnexio().getId();
catch (Exception e)
return 1L;
@Override
public void afterPropertiesSet()
// do nothing
// overridden to avoid datasource validation error by Spring
在 HibernateConfiguration 中这样使用:
@Configuration
@EnableTransactionManagement
public class HibernateConfiguration
@Autowired
private SecurityManager securitymanager;
@Autowired
private Parameters parameters;
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean()
LocalContainerEntityManagerFactoryBean lcemfb = new LocalContainerEntityManagerFactoryBean();
lcemfb.setDataSource(this.dataSource());
lcemfb.setPackagesToScan(new String[] "cat.itec.pgm.persistence" );
lcemfb.setPersistenceUnitName("pgmdb");
HibernateJpaVendorAdapter va = new HibernateJpaVendorAdapter();
va.setShowSql(true);
lcemfb.setJpaVendorAdapter(va);
Properties ps = new Properties();
ps.put("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
ps.put("hibernate.format_sql", "true");
ps.put("hibernate.show_sql", "true");
lcemfb.setJpaProperties(ps);
lcemfb.afterPropertiesSet();
return lcemfb;
@Bean
public DataSource dataSource()
RoutingDataSource rds = new RoutingDataSource();
Map<Long, DataSource> targetDataSources = new HashMap<Long, DataSource>();
List<Connexio> connexioLogins = new ArrayList<Connexio>();
try
connexioLogins = securitymanager.getConnexioLogins();
catch (Exception e)
System.out.println("Cannot Load List Of Connections");
for (Connexio c : connexioLogins)
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName(parameters.getDriverClassName());
ds.setUrl(generateUrlConnection(c));
ds.setUsername(c.getDbUsername());
ds.setPassword(c.getDbPassword());
targetDataSources.put(c.getId(), ds);
rds.setTargetDataSources(targetDataSources);
return rds;
@Bean
public PlatformTransactionManager transactionManager()
JpaTransactionManager tm = new JpaTransactionManager();
tm.setEntityManagerFactory(this.entityManagerFactoryBean().getObject());
return tm;
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation()
return new PersistenceExceptionTranslationPostProcessor();
private String generateUrlConnection(Connexio c)
StringBuilder sb = new StringBuilder();
sb.append("jdbc:oracle:thin:@");
sb.append(c.getServer());
sb.append(":");
sb.append(c.getPort());
sb.append(":");
sb.append(c.getSid());
return sb.toString();
关键是当我启动服务器时它会抛出一个:
Caused by: org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:685)
at cat.itec.security.persistence.dao.login.impl.LoginDaoImpl.getConnexioLogins(LoginDaoImpl.java:37)
不知道是怎么报错让RoutingDataSource得到每一个“Connexio”,还是没有正确配置。
任何帮助或评论将不胜感激。 (我会尽快发布任何其他需要更好理解的代码)。
提前致谢。
编辑(无用,参见 EDIT2):
像这样稍微改变两个冲突的数据库点:
@Bean
public DataSource dataSource()
RoutingDataSource rds = new RoutingDataSource();
Map<Long,DataSource> targetDataSources = new HashMap<Long,DataSource>();
Connexio c = new Connexio();
c.setDbPassword("XXXXXXXXX");
c.setDbUsername("XXX");
c.setId(1L);
c.setPort("XXXXXXX");
c.setServer("XXXXXXXX");
c.setSid("XXXX");
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName(parameters.getDriverClassName());
ds.setUrl(generateUrlConnection(c));
ds.setUsername(c.getDbUsername());
ds.setPassword(c.getDbPassword());
targetDataSources.put(c.getId(), ds);
rds.setTargetDataSources(targetDataSources);
return rds;
和
@Override
protected Long determineCurrentLookupKey()
return 1L;
使应用程序像此更改之前一样工作。所以在服务器启动时访问数据库似乎是一个问题。有什么想法吗?
EDIT2:
更改了第一学期添加的代码,以发布完整的工作代码作为示例。
【问题讨论】:
【参考方案1】:我发现问题出在我的道层。在服务器启动时无法访问当前会话,所以我做了类似的事情:
try
Session session = securitySessionFactory.getCurrentSession();
Criteria crit = session.createCriteria(clazz);
return (List<T>) crit.list();
catch (Exception e)
Session session = securitySessionFactory.openSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
Criteria crit = session.createCriteria(clazz);
List<T> list = (List<T>) crit.list();
session.disconnect();
session.close();
return list;
这样我可以正确填充 RoutingDataSources 并使数据源的数量有点动态(在数据库中填充一个新条目并简单地重新启动服务器)。
考虑到惰性映射将被禁用,因此评估需要设置为 FetchType.Eager 的内容可能很有用(如果需要用它初始化 1 个以上的包,则使用 FetchMode.Subselect)
我将编辑该问题,以便将此作为示例,为 Spring 3.1.3 配置路由“动态”数据源并使用注释。
【讨论】:
以上是关于带有注释和(动态)AbstractRoutingDataSource 的 Spring 3.1.3 + Hibernate 配置的主要内容,如果未能解决你的问题,请参考以下文章