Issue
I am running into org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: myapp.myapp.models.Contact.messages, could not initialize proxy - no Session. I have looked into these similar questions Hibernate: LazyInitializationException: failed to lazily initialize a collection of role. Could not initialize proxy - no Session and How to solve the “failed to lazily initialize a collection of role” Hibernate exception but none of them help my situation. I had spring auto config my datasource to where I didn't have this issue but I added another datasource connection and then created a config file for each data source to which now everything works as normal, as it did before but I keep getting this error thrown. I don't know what to do. Any help is appreciated.
My DB info in my properties file looked like this before I added the other DB
##############DBs##################
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database=default
#Myapp DB
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/myapp? verifyServerCertificate=false&useSSL=false&requireSSL=false
spring.datasource.username=myusername
spring.datasource.password=mypassword
Everything worked with no issues.
This is how everything is set up now.
Properties file
##############DBs##################
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database=default
#Myapp DB
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/myapp? verifyServerCertificate=false&useSSL=false&requireSSL=false
spring.datasource.username=myusername
spring.datasource.password=mypassword
#Other DB
spring.seconddatasource.driverClassName = com.mysql.jdbc.Driver
spring.seconddatasource.url = jdbc:mysql://localhost:3306/other
spring.seconddatasource.username=myusername
spring.seconddatasource.password=mypassword
###################################
Contact Entity:
@Entity
@Table(name = "contact")
public class Contact {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "contact")
private List<Messages> messages;
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public List<Messages> getMessages() {
return this.messages == null ? null : new ArrayList<>(this.messages);
}
public void setMessages(List<Messages> messages) {
this.messages = messages;
}
public void addMessage(Messages message) {
this.messages.add(message); // this is where the error is being thrown
}
}
Message Entity:
@Entity
@Table(name = "message")
public class Contact {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@ManyToOne
@JoinColumn(name = "contactId", nullable = false)
private Contact contact;
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public Contact getContact() {
return this.contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
}
New MyAppConfigClass(As soon as this was put in with other, the error started happening):
@ComponentScan
@Configuration
@EnableJpaRepositories(
basePackages = { "myapp.myapp" },
entityManagerFactoryRef = "myappEntityManagerFactory",
transactionManagerRef = "myappTransactionManager")
@EnableTransactionManagement
public class MyAppDBConfiguration {
@Autowired private ApplicationContext applicationContext;
@Bean(name = "myappExceptionTranslator")
public HibernateExceptionTranslator personnelHibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
@Bean(name = "myappTransactionManager")
public PlatformTransactionManager personnelTransactionManager() {
return new JpaTransactionManager(personnelEntityManagerFactory().getObject());
}
@Bean(name = "myappEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean personnelEntityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("myapp.myapp");
factory.setDataSource(myappDataSource());
factory.afterPropertiesSet();
return factory;
}
@Primary
@Bean(name = "myappDataConfig")
@ConfigurationProperties("spring.datasource")
public DataSourceProperties myappProperties() {
return new DataSourceProperties();
}
@Bean(name = "myappData", destroyMethod = "")
public DataSource myappDataSource() {
DataSourceProperties properties = myappProperties();
if (null != properties.getJndiName()) {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
DataSource source = lookup.getDataSource(properties.getJndiName());
excludeMBeanIfNecessary(source, "myappData");
return source;
} else {
return properties.initializeDataSourceBuilder().build();
}
}
private void excludeMBeanIfNecessary(Object candidate, String beanName) {
try {
MBeanExporter mbeanExporter = this.applicationContext.getBean(MBeanExporter.class);
if (JmxUtils.isMBean(candidate.getClass())) {
mbeanExporter.addExcludedBean(beanName);
}
} catch (NoSuchBeanDefinitionException ex) {
// No exporter. Exclusion is unnecessary
}
}
}
This is the OtherConfigClass(almost exactly the same):
@ComponentScan
@Configuration
@EnableJpaRepositories(
basePackages = { "myapp.other" },
entityManagerFactoryRef = "otherEntityManagerFactory",
transactionManagerRef = "otherTransactionManager")
@EnableTransactionManagement
public class OtherDBConfiguration {
@Autowired private ApplicationContext applicationContext;
@Bean(name = "otherExceptionTranslator")
public HibernateExceptionTranslator personnelHibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
@Bean(name = "otherTransactionManager")
public PlatformTransactionManager personnelTransactionManager() {
return new JpaTransactionManager(personnelEntityManagerFactory().getObject());
}
@Bean(name = "otherEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean personnelEntityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("myapp.other");
factory.setDataSource(otherDataSource());
factory.afterPropertiesSet();
return factory;
}
@Bean(name = "otherDataConfig")
@ConfigurationProperties("spring.seconddatasource")
public DataSourceProperties otherProperties() {
return new DataSourceProperties();
}
@Bean(name = "otherData", destroyMethod = "")
public DataSource textappotherDataSource() {
DataSourceProperties properties = myappProperties();
if (null != properties.getJndiName()) {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
DataSource source = lookup.getDataSource(properties.getJndiName());
excludeMBeanIfNecessary(source, "otherData");
return source;
} else {
return properties.initializeDataSourceBuilder().build();
}
}
private void excludeMBeanIfNecessary(Object candidate, String beanName) {
try {
MBeanExporter mbeanExporter = this.applicationContext.getBean(MBeanExporter.class);
if (JmxUtils.isMBean(candidate.getClass())) {
mbeanExporter.addExcludedBean(beanName);
}
} catch (NoSuchBeanDefinitionException ex) {
// No exporter. Exclusion is unnecessary
}
}
}
This is the Application class:
@EnableAutoConfiguration
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
So I'm assuming I'm missing something with the new config files that was done apart of the AutoConfig. That was the only change I made and it started throwing the error. Like I said above, the data is being saved into the database correctly but that error is still being thrown.
I don't know why this would be the case, an explanation would be very helpful.
Update:
ContactRepository:
@Repository
public interface ContactRepository extends JpaRepository<Contact, Long> {
}
MessagesRepository:
@Repository
public interface MessagesRepository extends JpaRepository<Messages, Long> {
}
ServiceClass:
@Service
public void serviceClass(long id) {
Contact contact = contactRepository.findOne(id);
Messages msg = new Messages();
msg.setContact(contact);
// do some work here
Messages savedMessage = messagesRepository.save(msg);
contact.addMessage(savedMessage);
contactRepository.save(contact);
Solution
You can lazy-load only within transaction context. Use @Transactional annotation on your service class:
@Service
@Transactional
public class Service {
public void serviceClass(long id) {
Contact contact = contactRepository.findOne(id);
Messages msg = new Messages();
msg.setContact(contact);
// do some work here
Messages savedMessage = messagesRepository.save(msg);
contact.addMessage(savedMessage);
contactRepository.save(contact);
}
}
Answered By - pkkoniec