Реализация весенних транзакций с JAX-RS и Hibernate - PullRequest
1 голос
/ 07 марта 2012

Фон

Я пытаюсь реализовать веб-сервис RESTful, используя Apache-CXF, который взаимодействует с базой данных для выполнения некоторых операций CRUD через Hibernate.Веб-служба использует и производит объекты в формате JSON с использованием провайдера JAX-RS Jackson.

В настоящее время я получаю "не удалось лениво инициализировать коллекцию ... ни один сеанс или сеанс не был закрыт" исключение, которое всплывает из провайдера Джексона, когда он пытается сериализовать объект ответа.

Я предполагал, что если я пометил метод обслуживания @Transactional, то сеанс будет доступен провайдеру Джексона, когда онответ сериализован, но это не так.

Вопрос

Как получить доступ к сеансу гибернации, когда Джексон прогуливается по объекту во время сериализации?

То, что я пробовал

Конфигурация пружины

<context:component-scan base-package="com.db.cif.mapper" />
<context:component-scan base-package="com.db.cif.mapper.repository" />
<context:annotation-config />

<tx:jta-transaction-manager>
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</tx:jta-transaction-manager>

<tx:annotation-driven />

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="cifmapper" />
    <property name="jpaDialect">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
    </property>
</bean>

<!-- JAX-RS Configuration -->
<jaxrs:server id="mappingService" address="/">
    <jaxrs:serviceBeans>
        <ref bean="mappingServiceBean" />
    </jaxrs:serviceBeans>
    <jaxrs:extensionMappings>
        <entry key="json" value="application/json" />
    </jaxrs:extensionMappings>
    <jaxrs:providers>
        <ref bean="jsonProvider" />
    </jaxrs:providers>
</jaxrs:server>

<bean id="mappingServiceBean" class="com.db.cif.mapper.MappingService" />

<bean id="jsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />

Служебный компонент

@Service("mappingService")
@Transactional
public class MappingService 
{
    private static final Logger logger = Logger.getLogger(MappingService.class);

    @Autowired
    @Qualifier("mappingRepository")
    private MappingRepository mappingRepository;

    @GET
    @Path("/collections/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Collection getCollection(@PathParam("id") String id)
    {
        if(logger.isDebugEnabled())
        {
            logger.debug(String.format("Invoked getCollection, Collection id: %s", id));
        }

        return this.mappingRepository.getCollection(id);
    }

    @POST
    @Path("/collections/")
    @Consumes(MediaType.APPLICATION_JSON)
    public Response addCollection(Collection collection)
    {
        if(logger.isDebugEnabled())
        {
            logger.debug(String.format("Invoked addCollection, Collection: %s", collection));
        }

        this.mappingRepository.createCollection(collection);

        return Response.ok(collection).build();
    }
}

Собрание компонента

@Entity
@Table(schema = "CIFMAPPER", name = "COLLECTION")
public class Collection implements Serializable
{
    private static final long serialVersionUID = 1579878442412232635L;

    @Id
    @Column(name = "ID")
    private String id;

    @Column(name = "SRC_ENDPT_ID", nullable = false, insertable = false, updatable = false)
    private long sourceEndpointId;

    @Column(name = "DEST_ENDPT_ID", nullable = false, insertable = false, updatable = false)
    private long destinationEndpointId;

    @Column(name = "DESCRIPTION")
    private String description;

    @Column(name = "START_DATE", nullable = false)
    private Date startDate;

    @Column(name = "END_DATE")
    private Date endDate;

    @ManyToOne(optional = false)
    @JoinColumn(name = "SRC_ENDPT_ID")
    private Endpoint source;

    @ManyToOne(optional = false)
    @JoinColumn(name = "DEST_ENDPT_ID")
    private Endpoint destination;

    @OneToMany(mappedBy = "collection", targetEntity = MappingGroup.class, fetch = FetchType.EAGER)
    private List<MappingGroup> mappingGroups;

//Getters and Setters Removed For Brevity
}

1 Ответ

5 голосов
/ 07 марта 2012

Хотя я полагаю, что есть способ заставить эту работу (используя Filter или перехватчик, который открывает и закрывает сеанс), я думаю, что правильный ответ: не иметь ленивых коллекций и прокси.Настройте сопоставления таким образом, чтобы у вас не было отложенных коллекций.

Альтернативный подход заключается в их ручной инициализации.Это часто сочетается с DTO - объектами, структура которых аналогична сущностям, которые используются в качестве ответов на другие компоненты (так что вы не можете напрямую выставлять сущности).Таким образом, ваши методы возвращают OrderDTO, а не Order, где DTO содержит столько полей, сколько вам нужно, чтобы вернуться к вызывающей стороне.Вы вручную переносите значения из сущности в DTO.

...