Spring 3.1.3 + 带注释和(动态)AbstractRoutingDataSource 的 Hibernate 配置
Spring 3.1.3 + Hibernate Configuration with annotations and with (Dynamic) AbstractRoutingDataSource
我正在尝试更改只有一个数据源的注解休眠,以便在数据库中保存尽可能多的数据源。为了使用户拥有其分配的连接,并添加新类型的连接,只需重新启动服务器(避免 .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",或者它没有正确配置。
如有任何帮助或评论,我们将不胜感激。 (我会尽快 post 编辑更好理解所需的任何其他代码)。
提前致谢。
编辑(无用,见 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;
}
使应用程序像此更改之前一样工作。所以在服务器启动时访问数据库似乎是一个问题。有什么想法吗?
编辑2:
将第一学期添加的代码更改为 post 完整的工作代码作为示例。
我发现问题出在我的道层。在服务器启动时无法访问当前会话,所以我做了类似的事情:
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 配置路由 "dynamic" 数据源的示例,并带有注释。
我正在尝试更改只有一个数据源的注解休眠,以便在数据库中保存尽可能多的数据源。为了使用户拥有其分配的连接,并添加新类型的连接,只需重新启动服务器(避免 .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",或者它没有正确配置。
如有任何帮助或评论,我们将不胜感激。 (我会尽快 post 编辑更好理解所需的任何其他代码)。
提前致谢。
编辑(无用,见 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;
}
使应用程序像此更改之前一样工作。所以在服务器启动时访问数据库似乎是一个问题。有什么想法吗?
编辑2:
将第一学期添加的代码更改为 post 完整的工作代码作为示例。
我发现问题出在我的道层。在服务器启动时无法访问当前会话,所以我做了类似的事情:
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 配置路由 "dynamic" 数据源的示例,并带有注释。