2015-11-23 2 views
3

Недавно я настроил второе соединение с БД в своем приложении, и все работает нормально, пока я не попытался получить лениво инициализированную коллекцию из одного объекта (IncomeTariff). У меня есть два отдельных источника данных, менеджеры сущностей и т. Д. Важно то, что LazyInitializationException возникает только тогда, когда я получаю сущность из недавно добавленной БД. Более того, когда я переместил таблицу с IncomeTariff в старую БД, я смог получить эту коллекцию без каких-либо проблем. Я пробовал много вещей: добавив @Transactional, используя Hibernate.initialize() и т. Д. В журналах просто выглядит, что hibernate закрывает сессию слишком рано, и я просто не могу заставить ее работать. Надеюсь, что фрагменты кода сделают это более понятным. КонфигурацияLazyInitializationException при подключении к двум базам данных

Источники данных:

<!-- data source 1 --> 
<bean id="dataSource1" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close"> 
    <property name="driverClassName" value="${mysql.jdbc.driver}" /> 
    <property name="url" value="${mysql.jdbc.uri}" /> 
    <property name="username" value="${mysql.jdbc.username}" /> 
    <property name="password" value="${mysql.jdbc.password}" /> 
    <property name="maxActive" value="${mysql.jdbc.maxActive}" /> 
    <property name="testOnBorrow" value="true" /> 
    <property name="testOnReturn" value="true" /> 
    <property name="testWhileIdle" value="true" /> 
    <property name="timeBetweenEvictionRunsMillis" value="30000" /> 
    <property name="validationQuery" value="select 1" /> 
    <property name="validationInterval" value="30000" /> 
    <property name="jdbcInterceptors" 
     value="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer" /> 
    <property name="jmxEnabled" value="true" /> 
    <property name="logAbandoned" value="true" /> 
    <property name="maxWait" value="10000" /> 
    <property name="minEvictableIdleTimeMillis" value="30000" /> 
    <property name="removeAbandoned" value="true" /> 
    <property name="removeAbandonedTimeout" value="1200" /> 
</bean> 

<bean id="defaultJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" 
    p:dataSource-ref="dataSource1" primary="true" /> 

<bean id="defaultNamedParameterJdbcTemplate" 
    class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> 
    <constructor-arg ref="dataSource1" /> 
</bean> 

<!-- data source 2 --> 
<bean id="dataSource2" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close"> 
    <property name="driverClassName" value="${mysql.jdbc.driver}" /> 
    <property name="url" value="${mysql.ds2.uri}" /> 
    <property name="username" value="${mysql.ds2.username}" /> 
    <property name="password" value="${mysql.ds2.password}" /> 
    <property name="maxActive" value="${mysql.jdbc.maxActive}" /> 
    <property name="testOnBorrow" value="true" /> 
    <property name="testOnReturn" value="true" /> 
    <property name="testWhileIdle" value="true" /> 
    <property name="timeBetweenEvictionRunsMillis" value="30000" /> 
    <property name="validationQuery" value="select 1" /> 
    <property name="validationInterval" value="30000" /> 
    <property name="jdbcInterceptors" 
     value="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer" /> 
    <property name="jmxEnabled" value="true" /> 
    <property name="logAbandoned" value="true" /> 
    <property name="maxWait" value="10000" /> 
    <property name="minEvictableIdleTimeMillis" value="30000" /> 
    <property name="removeAbandoned" value="true" /> 
    <property name="removeAbandonedTimeout" value="1200" /> 
</bean> 

JPA конфигурации для старой БД:

@Configuration 
@EnableJpaRepositories(basePackages = { "pl.application.first.repository"}) 
@EnableTransactionManagement 
public class JpaConfig { 

@Autowired 
@Qualifier("dataSource1") 
private DataSource dataSource; 

@Bean 
@Primary 
public EntityManagerFactory entityManagerFactory() { 
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); 
    vendorAdapter.setGenerateDdl(false); 
    vendorAdapter.setShowSql(false); 
    vendorAdapter.setDatabasePlatform("MYSQL"); 

    LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); 
    factoryBean.setDataSource(dataSource); 
    factoryBean.setPersistenceUnitName("unit1"); 
    factoryBean.setJpaProperties(prepareProperties()); 
    factoryBean.afterPropertiesSet(); 

    return factoryBean.getObject(); 
} 

@Bean 
@Primary 
public PlatformTransactionManager transactionManager() { 
    JpaTransactionManager txManager = new JpaTransactionManager(); 
    txManager.setEntityManagerFactory(entityManagerFactory()); 
    return txManager; 
} 

private Properties prepareProperties() { 
    Properties prop = new Properties(); 
    prop.put("hibernate.cache.use_second_level_cache", true); 
    prop.put("hibernate.cache.use_query_cache", false); 
    prop.put("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"); 
    prop.put("hibernate.cache.provider_class", "org.hibernate.cache.SingletonEhCacheProvider"); 
    prop.put("javax.persistence.validation.mode", "none"); 
    return prop; 
} 

} 

JPA конфигурации для новой БД:

@Configuration 
@EnableJpaRepositories(basePackages = { 
    "pl.application.second.repository" }, entityManagerFactoryRef = "secondEntityManagerFactory", transactionManagerRef = "secondTransactionManager") 
@EnableTransactionManagement 
public class NewJpaConfig { 

@Autowired 
@Qualifier("dataSource2") 
private DataSource dataSource; 

@Bean(name = "secondEntityManagerFactory") 
public EntityManagerFactory entityManagerFactory() { 
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); 
    vendorAdapter.setGenerateDdl(false); 
    vendorAdapter.setShowSql(true); 
    vendorAdapter.setDatabasePlatform("MYSQL"); 

    LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); 
    factoryBean.setDataSource(dataSource); 
    factoryBean.setJpaProperties(prepareProperties()); 
    factoryBean.setPackagesToScan("pl.application.operator", "pl.application.common.db.converters"); 
    factoryBean.setPersistenceUnitName("unit2"); 
    factoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class); 
    factoryBean.afterPropertiesSet(); 

    return factoryBean.getObject(); 
} 

@Bean(name = "secondTransactionManager") 
public PlatformTransactionManager transactionManager() { 
    JpaTransactionManager txManager = new JpaTransactionManager(); 
    txManager.setEntityManagerFactory(entityManagerFactory()); 
    return txManager; 
} 

private Properties prepareProperties() { 
    Properties prop = new Properties(); 
    prop.put("hibernate.cache.use_second_level_cache", true); 
    prop.put("hibernate.cache.use_query_cache", false); 
    prop.put("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"); 
    prop.put("hibernate.cache.provider_class", "org.hibernate.cache.SingletonEhCacheProvider"); 
    prop.put("javax.persistence.validation.mode", "none"); 
    prop.put("hibernate.connection.autocommit", "false"); 
    prop.put("javax.persistence.lock.timeout", "90000"); 
    return prop; 
} 

} 

IncomeTariff класс, который вызывает проблемы:

@Entity 
@Table(name = "income_tariff") 
public class IncomeTariff extends Entity { 

@OneToMany(mappedBy = "incomeTariff") 
private List<IncomeTariffEntry> incomeTariffEntries; 

public List<IncomeTariffEntry> getIncomeTariffEntries() { 
    return incomeTariffEntries; 
} 

public void setIncomeTariffEntries(List<IncomeTariffEntry> incomeTariffEntries) { 
    this.incomeTariffEntries = incomeTariffEntries; 
} 

    @Override 
public int hashCode() { 
    int hash = 7; 
    hash = 23 * hash + Objects.hashCode(this.getId()); 
    return hash; 
} 

@Override 
public boolean equals(Object obj) { 
    if (obj == null) { 
     return false; 
    } 
    if (getClass() != obj.getClass()) { 
     return false; 
    } 
    final IncomeTariff other = (IncomeTariff) obj; 
    if (!Objects.equals(this.getId(), other.getId())) { 
     return false; 
    } 
    return true; 
} 

} 

BillingAccount объект класса из старой БД, которая работает отлично:

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING) 
@Table(name = "api_billing_account") 
public class BillingAccount extends Entity { 


@OneToMany(mappedBy = "billingAccount") 
private List<User> users = new ArrayList<>(); 

public List<User> getUsers() { 
    return users; 
} 

public void setUsers(List<User> users) { 
    this.users = users; 
} 

} 

И IncomeTariffService, как вы можете видеть сейчас метод findAllTariffs() не имеет никакого смысла, я изменил его, чтобы показать, что выборка пользователей из BillingAccount отлично работает. Хранилища только обычные пружинные данных хранилища на основе:

@Service 
@Transactional 
public class IncomeTariffService { 

@Autowired 
private final IncomeTariffRepository incomeTariffRepository; // fetching data from new DB 

@Autowired 
private BillingAccountRepository billingAccountRepository; //fetching data from old DB 

private static final Logger logger = LoggerFactory.getLogger(IncomeTariffService.class); 

public List<IncomeTariff> findAllTariffs() { 
    BillingAccount findOne = billingAccountRepository.findOne(666000); 
    logger.info("TEST"); 
    logger.info("" + findOne.getUsers().size()); 
    return null; 
} 

public List<IncomeTariffEntry> findPositions(Integer tariffId) { 
    IncomeTariff findOne = incomeTariffRepository.findOne(tariffId); 
    logger.info("TEST"); 
    List<IncomeTariffEntry> incomeTariffEntries = findOne.getIncomeTariffEntries(); 
    logger.info("" + incomeTariffEntries.size()); 
    return incomeTariffEntries; 
} 

} 

А вот что происходит в журналах, когда я выборка пользователей Список frombilling счет:

11:33:48.988 [http-nio-8080-exec-33] TRACE org.hibernate.type.CollectionType - Created collection wrapper: [pl.application.billing.model.BillingAccount.users#666000] 
11:33:48.989 [http-nio-8080-exec-33] INFO p.s.a.c.t.s.IncomeTariffService - TEST 
11:33:48.990 [http-nio-8080-exec-33] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [INTEGER] - [666000] 
11:33:48.997 [http-nio-8080-exec-33] INFO p.s.a.c.t.s.IncomeTariffService - 0 
11:33:48.997 [http-nio-8080-exec-33] DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction commit 
11:33:48.997 [http-nio-8080-exec-33] DEBUG o.s.orm.jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [[email protected]] 
11:33:48.999 [http-nio-8080-exec-33] DEBUG o.s.orm.jpa.JpaTransactionManager - Not closing pre-bound JPA EntityManager after transaction 
11:33:49.001 [http-nio-8080-exec-33] DEBUG o.s.o.j.s.OpenEntityManagerInViewFilter - Closing JPA EntityManager in OpenEntityManagerInViewFilter 
11:33:49.001 [http-nio-8080-exec-33] DEBUG o.s.o.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager 

Я могу получить размер сбора пользователей и, похоже, сделка закрыта в конце метода

Вот что происходит, когда я выборка IncomeTariff (без @Transactional аннотации):

13:09:31.285 [http-nio-8080-exec-51] TRACE org.hibernate.type.CollectionType - Created collection wrapper: [pl.application.operator.pricelist.income.IncomeTariff.incomeTariffEntries#1] 
13:09:31.288 [http-nio-8080-exec-51] DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction commit 
13:09:31.288 [http-nio-8080-exec-51] DEBUG o.s.orm.jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [[email protected]] 
13:09:31.316 [http-nio-8080-exec-51] DEBUG o.s.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [[email protected]] after transaction 
13:09:31.317 [http-nio-8080-exec-51] DEBUG o.s.o.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager 
13:09:31.317 [http-nio-8080-exec-51] INFO p.s.a.c.t.s.IncomeTariffService - TEST 
13:09:31.342 [http-nio-8080-exec-51] ERROR p.s.a.r.c.e.GlobalExceptionHandler - failed to lazily initialize a collection of role: pl.application.operator.pricelist.income.IncomeTariff.incomeTariffEntries, could not initialize proxy - no Session 

и с аннотацией @Transactional:

12:24:14.599 [http-nio-8080-exec-42] TRACE org.hibernate.type.CollectionType - Created collection wrapper: [pl.application.operator.pricelist.income.IncomeTariff.incomeTariffEntries#1] 
12:24:14.600 [http-nio-8080-exec-42] DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction commit 
12:24:14.600 [http-nio-8080-exec-42] DEBUG o.s.orm.jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [[email protected]] 
12:24:14.601 [http-nio-8080-exec-42] DEBUG o.s.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [[email protected]] after transaction 
12:24:14.601 [http-nio-8080-exec-42] DEBUG o.s.o.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager 
12:24:14.601 [http-nio-8080-exec-42] DEBUG o.s.orm.jpa.JpaTransactionManager - Resuming suspended transaction after completion of inner transaction 
12:24:14.601 [http-nio-8080-exec-42] INFO p.s.a.c.t.s.IncomeTariffService - TEST 
12:24:14.604 [http-nio-8080-exec-42] DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction rollback 
12:24:14.604 [http-nio-8080-exec-42] DEBUG o.s.orm.jpa.JpaTransactionManager - Rolling back JPA transaction on EntityManager [[email protected]] 
12:24:14.606 [http-nio-8080-exec-42] DEBUG o.s.orm.jpa.JpaTransactionManager - Not closing pre-bound JPA EntityManager after transaction 
12:24:14.612 [http-nio-8080-exec-42] ERROR p.s.a.r.c.e.GlobalExceptionHandler - failed to lazily initialize a collection of role: pl.application.operator.pricelist.income.IncomeTariff.incomeTariffEntries, could not initialize proxy - no Session 

Сделка была закрыта до «TEST» даже вошел, а затем это просто невозможно, чтобы получить коллекцию от лица. Как я уже упоминал ранее, когда я переместил доход в старую БД, все работало, поэтому, возможно, что-то не так с моей конфигурацией, но я борюсь с этим в течение двух дней, и у меня нет идей.

+0

присоединиться принести свою коллекцию в запросе непосредственно –

ответ

1

Поскольку вы используете 2 менеджер транзакций вы можете посмотреть в указание, какой из них вы хотите использовать в @Transactional аннотации (см http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#tx-multiple-tx-mgrs-with-attransactional)

Например, я бы удалить @Transactional на уровне класса и добавьте диспетчер транзакций специфический @Transactional() над каждым методом поиска.

Как:

@Transactional("secondTransactionManager) 
public List<IncomeTariff> findAllTariffs() {} 

С сделкой на уровне услуг, если сбор достигается за пределами границ транзакционных вы получите такое исключение. Попробуйте установить коллекцию без лени или переместите транзакцию выше, например бизнес-уровень.

Надеется, что это помогает

А.

+0

Спасибо большого! Добавление менеджера транзакций в @Transactional помогло :) – Gibson001

Смежные вопросы