无法延迟初始化角色集合:myapp.myapp.models.Contact.messages,无法初始化代理-没有会话

时间:2018-12-28 20:56:00

标签: hibernate spring-boot jpa spring-data-jpa

我遇到org.hibernate.LazyInitializationException:无法延迟初始化角色集合:myapp.myapp.models.Contact.messages,无法初始化代理-没有会话。我已经研究了Hibernate: LazyInitializationException: failed to lazily initialize a collection of role. Could not initialize proxy - no SessionHow 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);

4 个答案:

答案 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