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" 数据源的示例,并带有注释。