我遇到org.hibernate.LazyInitializationException:无法延迟初始化角色集合:myapp.myapp.models.Contact.messages,无法初始化代理-没有会话。我已经研究了Hibernate: LazyInitializationException: failed to lazily initialize a collection of role. Could not initialize proxy - no Session和How to solve the “failed to lazily initialize a collection of role” Hibernate exception这些类似的问题,但没有一个对我的情况有所帮助。我将spring数据源自动配置到我没有这个问题的位置,但是我添加了另一个数据源连接,然后为每个数据源创建了一个配置文件,现在一切正常,就像以前一样,但是我一直收到此错误抛出。我不知道该怎么办。任何帮助表示赞赏。
在添加另一个数据库之前,我的属性文件中的数据库信息看起来像这样
##############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
一切正常,没有问题。
这就是现在所有设置的方式。
属性文件
##############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
###################################
联系人实体:
@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
}
}
消息实体:
@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;
}
}
新的MyAppConfigClass(与其他应用程序一并使用,就会开始发生错误):
@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
}
}
}
这是OtherConfigClass(几乎完全相同):
@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
}
}
}
这是Application类:
@EnableAutoConfiguration
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
因此,我假设我在AutoConfig之外完成的新配置文件中缺少某些内容。那是我所做的唯一更改,并且开始引发错误。就像我上面说的那样,数据已正确保存到数据库中,但仍然抛出该错误。
我不知道为什么会这样,解释会很有帮助。
更新:
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);
答案 0 :(得分:2)
有几种方法可以在Hibernate中初始化惰性关联,但最重要的是,您应该能够使用FETCH JOIN
通过(1)解决该问题(如果使用Criteria API,或使用自定义查询(无论是本地查询还是非本地查询))或(2)(使用已命名/动态的实体图)。
在Contact
中尝试类似的操作:
@Entity
@Table(name = "contact")
@NamedEntityGraph(name = "graph.Contact.messages",
attributeNodes = @NamedAttributeNode("messages"))
public class Contact { ... }
如果您有权(并且有时间)使用EntityManager
,那么所有这些都非常简单。我记得Spring Data JPA在某些方面存在一些问题,尤其是在Entity Graphs方面,但是我认为对于您的情况应该可以立即使用。
您还可以尝试使用FETCH JOIN
之类的SELECT c FROM Contact AS c JOIN FETCH c.messages m WHERE c.id = :id"
–可能需要进行调整,我只是在“即时”上写了它。
注意:另外,由于您使用的是Spring,因此请确保您的“服务”类的注释为@Transactional
,因为延迟加载仅适用于(相同)交易环境。
请不要将您的
FetchType.LAZY
变成FetchType.EAGER
–除非您要丢掉工作;)同时,在映射关系上调用方法是很容易的事,它会带您进入所谓的
N + 1
问题。
答案 1 :(得分:1)
您只能在事务上下文内延迟加载。 在服务类上使用@Transactional批注:
@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);
}
}
答案 2 :(得分:1)
在 application.properties 文件中设置 enable_lazy_load_no_trans=true 解决了我的问题,而无需将获取类型从懒惰更改为急切。
答案 3 :(得分:0)
如果您在application.properties配置文件中添加了以下内容,则应该可以使用
spring.jpa.open-in-view=false