Не удалось записать содержимое: не удалось лениво инициализировать набор ролей с помощью OpenEntityManagerInViewFilter.

Итак, у меня есть два класса:

Образец

@Entity
@Table(name="sample")
public class Sample implements Serializable {

@Id
@GeneratedValue
@Column(name="sample_id")
private Long sample_id;
@Column(name="id")
private String id;
@Column(name="description")
private String description;
@ManyToOne
@JoinColumn(name="dna_study_id")
private DNA_Study study;
...Getters and setters ...

ДНК_исследование

@Entity
@Table(name = "dna_study")
public class DNA_Study implements Serializable {

@Id
@GeneratedValue
@Column(name="dna_study_id")
private Long id;
@Column(name="name")
private String name;
@Column(name="description")
private String description;
@Column(name="date")
private Date date;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "dna_study_id")
private List<Sample> samples;

Я хочу получить все данные DNA_Study из своей базы данных, имея этот DAO:

@Repository
public interface DNA_StudyDAO extends CrudRepository<DNA_Study, Long>{ }

и этот RestController:

@RestController
public class AnalysisController {

    ClassPathXmlApplicationContext context;

    @CrossOrigin
    @RequestMapping("/getanalysis")
    public ArrayList<DNA_Study> getAnalysis() {

        context = new ClassPathXmlApplicationContext("applicationContext.xml");
        DNA_StudyDAO dao = context.getBean(DNA_StudyDAO.class);
        return (ArrayList<DNA_Study>) dao.findAll();
    }

Когда я вызываю его, я получаю «Не удалось записать контент: не удалось лениво инициализировать набор ролей». Я попытался изменить свой DAO, чтобы метод findAll() изменился на:

@Override
@Query("select d from DNA_Study d join fetch d.samples")
Iterable<DNA_Study> findAll();

при использовании этого исключения не выдается, но вызывающий метод создает бесконечный цикл, поскольку создание DNA_Study подразумевает загрузку его образцов, и каждый образец загружает его DNA_Study и т. д., поэтому он прерывается. Итак, я предполагаю, что мне нужно будет добавить OpenEntityManagerInViewFilter и отменить переопределение для findAll(), попытавшись добавить в мой класс SpringBootServletInitializer:

@Override
public void onStartup(ServletContext servletContext) throws ServletException
{
    AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
    rootContext.register(Application.class);
    rootContext.setServletContext(servletContext);
    ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));
    dispatcher.setLoadOnStartup(1);
    dispatcher.addMapping("/");

    FilterRegistration.Dynamic filter = servletContext.addFilter("openEntityManagerInViewFilter", OpenEntityManagerInViewFilter.class);
    filter.setInitParameter("singleSession", "true");
    filter.addMappingForServletNames(null, true, "dispatcher");
    servletContext.addListener(new ContextLoaderListener(rootContext));
}

Но когда я звоню, я все равно получаю "не удалось лениво инициализировать набор ролей"

как мне правильно добавить OpenEntityManagerInViewFilter?


ИЗМЕНИТЬ

класс, который расширяет SpringBootServletInitializer:

@Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(GemDomusServerApplication.class);
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException
    {
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
        rootContext.register(Application.class);
        rootContext.setServletContext(servletContext);
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
        FilterRegistration.Dynamic filter = servletContext.addFilter("openEntityManagerInViewFilter", OpenEntityManagerInViewFilter.class);
        filter.setInitParameter("singleSession", "true");
        filter.setInitParameter("entityManagerFactoryBeanName", "entityManagerFactory");
        filter.setInitParameter("flushMode", "auto");
        filter.addMappingForServletNames(null, true, "dispatcher");
        servletContext.addListener(new ContextLoaderListener(rootContext));
        servletContext.addListener(new RequestContextListener());
    }

    public static void main(String[] args) {

        SpringApplication.run(GemDomusServerApplication.class, args);
    }

задействованные компоненты applicationContext.xml

<bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="persistenceUnitName" value="jpaData" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
<bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName">
            <value>org.postgresql.Driver</value>
        </property>
        <property name="url">
            <value>**</value>
        </property>
        <property name="username">
            <value>**</value>
        </property>
        <property name="password">
            <value>**</value>
        </property>
    </bean>

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

    <persistence-unit name="jpaData"/>

</persistence>

person Dexter    schedule 03.03.2016    source источник


Ответы (2)


Ваша проблема очень похожа на эту Отложенная инициализация с помощью OpenEntityManagerInViewFilter?

Другой вариант, который я предпочитаю, - это вызвать Hibernate.initialize(Object) внутри вашего DAO или менеджера, обертывающего ваш DAO с аннотацией @Transactional.

person Filippo Fratoni    schedule 03.03.2016
comment
Итак, я должен вызвать findAll() и перебрать любой элемент с помощью Hiebernate.initialize(Object)? - person Dexter; 03.03.2016
comment
этого запроса должно быть достаточно, чтобы заставить EAGER получить ваши коллекции образцов, выберите d из DNA_Study d join fetch d.samples, но в случае, если это не сработает, вы можете принудительно выполнить это в коде, итерируя объект и вызывая Hibernate.initialize - person Filippo Fratoni; 03.03.2016
comment
Но мне нужна ленивая инициализация. В ссылку добавляют фильтр, у меня так и не получилось. - person Dexter; 04.03.2016
comment
Также выполняется ArrayList‹DNA_Study› result = (ArrayList‹DNA_Study›) dao.findAll(); for(DNA_Study dna: результат) {Hibernate.initialize(dna.getSamples()); } возвращает коллекцию HibernateException, не связанную ни с одним сеансом - person Dexter; 04.03.2016
comment
Это работает для меня в моем проекте. Попробуйте. Это принудительно инициализирует итератор. if (dna.getSamples().size() › 0) dna.getSamples().iterator().hasNext(); - person Filippo Fratoni; 07.03.2016

Что ж, наконец, после попытки это сработало: создание коллекций кабины инициализации Eager (Sample и DNA_Study и изменение AnalysisController на:

    @CrossOrigin
    @RequestMapping("/getanalysis")
    public JsonArray getAnalysis() {

        context = new ClassPathXmlApplicationContext("applicationContext.xml");
        DNA_StudyDAO dao = context.getBean(DNA_StudyDAO.class);
        ArrayList<DNA_Study> result = (ArrayList<DNA_Study>) dao.findAll();
        JsonArrayBuilder datasourcesBuilder = Json.createArrayBuilder();
        for(DNA_Study study : result) {
            datasourcesBuilder
            .add(Json.createObjectBuilder()
                .add("name", study.getName())
                .add("description", study.getDescription())
                .add("size", String.valueOf(study.getSamples().size())));
        }
        return datasourcesBuilder.build();
}

Не удалось найти способ сделать это с ленивой инициализацией

person Dexter    schedule 07.03.2016