I've got a problem that matches this thread:
Spring 3.0.5
postgreSQL 8.4
hibernate 3.x
I want to use that nice RESTful web controller mechanism with a POST request writing into a @Lob @Basic property in an entity object in a Map<String, DocumentContent>.
Because postgreSQL-driver wants me to open a transaction when loading LOB map contents sending POST data (languageContents['1'].)content['content'].content=sth produces the following exception:
Code:
org.springframework.beans.InvalidPropertyException: Invalid property 'languageContents[1].contents[content]' of bean class [de.algorythm.cmf.model.entity.Document]: Illegal attempt to get property 'contents' threw exception; nested exception is org.hibernate.exception.GenericJDBCException: could not initialize a collection: [de.algorythm.cmf.model.entity.DocumentLanguageContent.contents#1]
at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:827)
...
Caused by: org.hibernate.exception.GenericJDBCException: could not initialize a collection: [de.algorythm.cmf.model.entity.DocumentLanguageContent.contents#1]
at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:140)
...
Caused by: org.postgresql.util.PSQLException: LargeObjects (LOB) dürfen im Modus 'auto-commit' nicht verwendet werden.
at org.postgresql.largeobject.LargeObjectManager.open(LargeObjectManager.java:200)
at org.postgresql.largeobject.LargeObjectManager.open(LargeObjectManager.java:172)
at org.postgresql.jdbc2.AbstractJdbc2BlobClob.<init>(AbstractJdbc2BlobClob.java:47)
at org.postgresql.jdbc2.AbstractJdbc2Clob.<init>(AbstractJdbc2Clob.java:25)
at org.postgresql.jdbc3.AbstractJdbc3Clob.<init>(AbstractJdbc3Clob.java:20)
at org.postgresql.jdbc3.Jdbc3Clob.<init>(Jdbc3Clob.java:18)
at org.postgresql.jdbc3.Jdbc3ResultSet.getClob(Jdbc3ResultSet.java:43)
at org.postgresql.jdbc2.AbstractJdbc2ResultSet.getClob(AbstractJdbc2ResultSet.java:384)
at org.apache.commons.dbcp.DelegatingResultSet.getClob(DelegatingResultSet.java:568)
at org.apache.commons.dbcp.DelegatingResultSet.getClob(DelegatingResultSet.java:568)
at org.hibernate.type.descriptor.sql.ClobTypeDescriptor$2.doExtract(ClobTypeDescriptor.java:70)
at org.hibernate.type.descriptor.sql.BasicExtractor.extract(BasicExtractor.java:64)
at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:253)
at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:249)
at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:229)
at org.hibernate.type.AbstractStandardBasicType.hydrate(AbstractStandardBasicType.java:330)
at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:2265)
at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1527)
at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1455)
at org.hibernate.loader.Loader.getRow(Loader.java:1355)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:611)
at org.hibernate.loader.Loader.doQuery(Loader.java:829)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274)
at org.hibernate.loader.Loader.loadCollection(Loader.java:2166)
... 85 more
My Entities look as follows:
Code:
@Entity
public class Document {
@OneToMany(mappedBy="document", targetEntity=DocumentLanguageContent.class, cascade={CascadeType.REMOVE})
@MapKey(name="language")
private Map<Language, DocumentLanguageContent> languageContents;
// @id, getter, setter ...
}
@Entity
public class DocumentLanguageContent {
@OneToMany(cascade={CascadeType.REMOVE})
@MapKey(name="name")
private Map<String, DocumentContent> contents;
// @id, getter, setter ...
}
@Entity
public class DocumentContent {
@Lob
@Basic
private String content;
@Version
private int version;
// @id, getter, setter ...
}
The Controller:
Code:
@RequestMapping("/system/{type}")
@Controller
public class CrudController {
@RequestMapping(method = RequestMethod.POST)
@Transactional
public String edit(@PathVariable("type") final String type,
@ModelAttribute("command") @Valid Object entity,
BindingResult result, Model model,
HttpServletRequest req)
throws Exception {
//...
}
// ...
}
My OR-Mapper config in applicationContext.xml:
Code:
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
<property name="driverClassName" value="${database.driverClassName}"/>
<property name="url" value="${database.url}"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
I know that probably this is not the most performant way but I am implementing an annotation-driven form generator. Reference types in backend form entities that have no backend defined by configuration are part of parent entity's form.
Simply, the target is to get a form generated for every wanted entity at any cost which can be modified later.
Needless to say: this is academic.
To solve this problem I could open a transaction and read the map without problems but the map is read by the spring framework right before my controller method is called with the command so that I cannot open a transaction.
Probably, I haven't understand the spring concept but sending map values as URL parameter is supported so I am assuming this use case should also work?!
Edit: Of course, I could and should CRUD referenced entities in an extra controller. Hibernate validator can guarantee back references are not nullable.
But this would result in having problems with forms and sub forms with different actions.
BUT if I have an embeddable object referenced at such entity I cannot CRUD it directly with a controller. The @Version field is the only reason why DocumentContent is an entity in this case. Else it would be annotated with @Embeddable. If we have this case we are stuck on the described problem!
Is there any way to solve this problem? Help, please!!!
regards,
Max