Próbuję napisać klasy implementującej UserType do obsługi tablic w Hibernacja/JPA. Użyłem następujących stanowisk mapping a postgres array with hibernate i Hibernate/JPA/HSQL : How to create a Dialect mapping for User Type ARRAY do zbudowania rozwiązania, ale nie mogę go uruchomić. Stworzyłem nowy projekt Spring Roo, aby go przetestować. Oto różne formaty (wszystkie klasy Java znajdują się w teście pakietu):Tablica z UserType w Hibernate i PostgreSQL -> MappingException
persistence.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <!-- <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/> --> <property name="hibernate.dialect" value="test.PostgreSQLDialectArray"/> <!-- value="create" to build a new database on each run; value="update" to modify an existing database; value="create-drop" means the same as "create" but also drops tables when Hibernate closes; value="validate" makes no changes to the database --> <property name="hibernate.hbm2ddl.auto" value="create"/> <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy"/> <property name="hibernate.connection.charSet" value="UTF-8"/> <!-- Uncomment the following two properties for JBoss only --> <!-- property name="hibernate.validator.apply_to_ddl" value="false" /--> <!-- property name="hibernate.validator.autoregister_listeners" value="false" /--> </properties> </persistence-unit> </persistence>
TestArray.java
package test; import java.math.BigInteger; import java.security.SecureRandom; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.roo.addon.javabean.RooJavaBean; import org.springframework.roo.addon.serializable.RooSerializable; import org.springframework.roo.addon.tostring.RooToString; @RooJavaBean @RooToString @RooSerializable public class TestArray { private static final long serialVersionUID = 1L; private SecureRandom random = new SecureRandom(); public String nextSessionId() { return new BigInteger(130, random).toString(32); } public static void main(String[] args) { ApplicationContext context; context = new ClassPathXmlApplicationContext("classpath:META-INF/spring/applicationContext.xml"); int[] array = new int[1428]; TestArray test = new TestArray(); Blabla blabla = new Blabla(); int nb = 1428; for(int i = 0 ; i < nb ; i++) array[i] = test.random.nextInt(); // blabla.setTest(array); // blabla.persist(); // System.out.println(Arrays.toString(blabla.getTest())); System.out.println(java.sql.Types.ARRAY); System.out.println("Done"); } }
Blabla.java
package test; import org.hibernate.annotations.Type; import org.springframework.roo.addon.entity.RooEntity; import org.springframework.roo.addon.javabean.RooJavaBean; import org.springframework.roo.addon.tostring.RooToString; @RooJavaBean @RooToString @RooEntity public class Blabla { @Type(type = "test.IntArrayUserType") private int[] array; }
PostgreSQLDialectArray
package test; import java.sql.Types; public class PostgreSQLDialectArray extends org.hibernate.dialect.PostgreSQLDialect{ public PostgreSQLDialectArray() { super(); registerHibernateType(Types.ARRAY, "array"); } }
IntArrayUserType.java (w zasadzie takie same, jak w mapping a postgres array with hibernate)
package test; import java.io.Serializable; import java.sql.Array; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.hibernate.HibernateException; import org.hibernate.usertype.UserType; public class IntArrayUserType implements UserType { protected static final int SQLTYPE = java.sql.Types.ARRAY; private int[] toPrimitive(Integer[] array){ int[] a = new int[array.length]; for(int i = 0 ; i < array.length ; i++) a[i] = array[i]; return a; } private Integer[] toObject(int[] array){ Integer[] a = new Integer[array.length]; for(int i = 0 ; i < array.length ; i++) a[i] = array[i]; return a; } @Override public Object nullSafeGet(final ResultSet rs, final String[] names, final Object owner) throws HibernateException, SQLException { Array array = rs.getArray(names[0]); Integer[] javaArray = (Integer[]) array.getArray(); return toPrimitive(javaArray); } @Override public void nullSafeSet(final PreparedStatement statement, final Object object, final int i) throws HibernateException, SQLException { System.out.println("test null safe set..."); Connection connection = statement.getConnection(); int[] castObject = (int[]) object; Integer[] integers = toObject(castObject); Array array = connection.createArrayOf("integer", integers); statement.setArray(i, array); System.out.println("test null safe set..."); } @Override public Object assemble(final Serializable cached, final Object owner) throws HibernateException { return cached; } @Override public Object deepCopy(final Object o) throws HibernateException { return o == null ? null : ((int[]) o).clone(); } @Override public Serializable disassemble(final Object o) throws HibernateException { return (Serializable) o; } @Override public boolean equals(final Object x, final Object y) throws HibernateException { return x == null ? y == null : x.equals(y); } @Override public int hashCode(final Object o) throws HibernateException { return o == null ? 0 : o.hashCode(); } @Override public boolean isMutable() { return false; } @Override public Object replace(final Object original, final Object target, final Object owner) throws HibernateException { return original; } @Override public Class<int[]> returnedClass() { return int[].class; } @Override public int[] sqlTypes() { return new int[] { SQLTYPE }; } }
A teraz StackTrace:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager' defined in class path resource [META-INF/spring/applicationContext.xml]: Cannot resolve reference to bean 'entityManagerFactory' while setting bean property 'entityManagerFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [META-INF/spring/applicationContext.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: persistenceUnit] Unable to build EntityManagerFactory
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:106)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1325)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1086)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:580)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at test.TestArray.main(TestArray.java:29)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [META-INF/spring/applicationContext.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: persistenceUnit] Unable to build EntityManagerFactory
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1420)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:322)
... 15 more
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: persistenceUnit] Unable to build EntityManagerFactory
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:915)
at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:74)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:225)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:308)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
... 22 more
Caused by: org.hibernate.MappingException: No Dialect mapping for JDBC type: 2003
at org.hibernate.dialect.TypeNames.get(TypeNames.java:77)
at org.hibernate.dialect.TypeNames.get(TypeNames.java:100)
at org.hibernate.dialect.Dialect.getTypeName(Dialect.java:296)
at org.hibernate.mapping.Column.getSqlType(Column.java:208)
at org.hibernate.mapping.Table.sqlCreateString(Table.java:418)
at org.hibernate.cfg.Configuration.generateSchemaCreationScript(Configuration.java:1099)
at org.hibernate.tool.hbm2ddl.SchemaExport.<init>(SchemaExport.java:106)
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:372)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1872)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:906)
... 27 more
Więc myślę, że dialekt który stworzyłem nie jest w ogóle używany lub jest źle, ale nie wiem dlaczego. Myślę, że adnotacja @ Type powinna wykonać mapowanie, ale widziałem kilka tagów, ale nie wiem, czy są one potrzebne w tym przykładzie, a jeśli tak, to gdzie je mam? Minęły dwa dni, kiedy utknąłem z powodu tego problemu i jestem zdesperowany. Czy możesz mi pomóc w debugowaniu tego programu? Proszę. Dziękuję bardzo z góry.
Edit 0:
Plik persistence.xml wydają się znaleźć poprawne dialekt ale getTypeName function (2003) wyrzuca błędy powyżej po zrobieniu registerHibernateType (Types.ARRAY "tablicy"). BTW, używam Hibernate 3.6.4.Final i postgresql 8.4-702.jdbc3.
Edycja 1:
że dodał następujące konstruktorowi PostgreSQLDialectArray:
registerColumnType(Types.ARRAY, "integer[$l]");
który wydaje się częściowo rozwiązuje problem. Jednak teraz mam kolejny błąd:
2013-01-09 11:14:30,281 [main] ERROR org.hibernate.tool.hbm2ddl.SchemaExport - Unsuccessful: create table blabla (id int8 not null, array int[255], name varchar(255), test int4 not null, version int4, primary key (id))
2013-01-09 11:14:30,282 [main] ERROR org.hibernate.tool.hbm2ddl.SchemaExport - ERREUR: erreur de syntaxe sur ou près de « array »
Position: 40
Widocznie, Hibernate nadal nie wie, w jaki sposób utworzyć tabelę z tablicą w nim ...
Edit 2:
Wydaje to postgresql nie podobało mi się, że moja kolumna nazywała się "tablica". Zmieniam to i zadziałało. Tabela jest tworzona przez hibernację z tablicą liczb całkowitych.
BUT Nie mogę zapisać żadnej tablicy w tym hibernacji z powodu problemu z implementacją UserType. Najwyraźniej tworzenie tablicy kończy się niepowodzeniem z createArrayOf. Czytam niektóre wątki w tej sprawie, aby uzyskać dostęp do podstawowego połączenia zamiast opakowania. Myślę, że zamierzam otworzyć nowy wątek łączący się z tym, ponieważ ten problem jest zupełnie inny.
StackTrace:
Exception in thread "main" java.lang.AbstractMethodError: org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.createArrayOf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/sql/Array;
at test.IntArrayUserType.nullSafeSet(IntArrayUserType.java:59)
at org.hibernate.type.CustomType.nullSafeSet(CustomType.java:140)
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2184)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2430)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2874)
at org.hibernate.action.EntityInsertAction.execute(EntityInsertAction.java:79)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:184)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:76)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:467)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$afterReturning$org_springframework_transaction_aspectj_AbstractTransactionAspect$3$2a73e96c(AbstractTransactionAspect.aj:78)
at test.Blabla_Roo_Entity.ajc$interMethod$test_Blabla_Roo_Entity$test_Blabla$persist(Blabla_Roo_Entity.aj:56)
at test.Blabla.persist(Blabla.java:1)
at test.Blabla_Roo_Entity.ajc$interMethodDispatch1$test_Blabla_Roo_Entity$test_Blabla$persist(Blabla_Roo_Entity.aj)
at test.TestArray.main(TestArray.java:39)
Edit 3:
Wreszcie, po następujących modyfikacjach UserType dla tablic całkowitych działa:
Dodaj tę linię w ApplicationContext. xml w:
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
....
<property name="accessToUnderlyingConnectionAllowed" value="true" />
</bean>
Modyfikacja nullSafeSet z IntArrayUserType
@Override public void nullSafeSet(final PreparedStatement statement, final Object object, final int i) throws HibernateException, SQLException { Connection connection = statement.getConnection(); int[] castObject = (int[]) object; Integer[] integers = toObject(castObject); Connection conn = ((DelegatingConnection) connection).getInnermostDelegate(); Array array = conn.createArrayOf("integer", integers); statement.setArray(i, array); }
ale wciąż jest to problemem, gdy coraz wszystkie wpisy z blabla tabeli: funkcja findAllBlablas nie działa poprawnie i wraca tylko pierwszy wpis ...
Edycja 4:
W rzeczywistości, to było wspaniałe, ale konsola zaćmienie nie był w stanie wydrukować wszystkie dane. To wszystko!
możliwe duplikat [odwzorowanie macierzy postgres'a z hibernacji] (http://stackoverflow.com/questions/1647583/mapping-a-postgres-array-with-hibernate) – borchvm