在Hibernate和PostgreSQL中使用UserType的数组 – > MappingException

我正在尝试编写一个实现UserType的类来处理Hibernate / JPA中的数组。 我使用以下post使用hibernate和Hibernate / JPA / HSQL 映射postgres数组 :如何为用户类型ARRAY创建Dialect映射以构建解决方案,但我无法使其工作。 我创建了一个新的Spring Roo项目来测试它。 以下是不同的文件(所有java类都位于包测试中):

  • persistence.xml中

       org.hibernate.ejb.HibernatePersistence  <!--  -->            
  • 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(基本上与使用hibernate映射postgres数组时相同)

     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 returnedClass() { return int[].class; } @Override public int[] sqlTypes() { return new int[] { SQLTYPE }; } } 

现在堆栈跟踪:

 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.(ClassPathXmlApplicationContext.java:139) at org.springframework.context.support.ClassPathXmlApplicationContext.(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.(SchemaExport.java:106) at org.hibernate.impl.SessionFactoryImpl.(SessionFactoryImpl.java:372) at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1872) at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:906) ... 27 more 

所以我想我创造的方言根本没用或者错了,但我不知道为什么。 我认为@Type注释应该进行映射,但是我看到了一些标签,但是我不知道它们是否在这个例子中是否需要,如果它们是,我在哪里有它们? 已经有两天我遇到了这个问题而且我变得绝望了。 你能帮我调试这个程序吗? 请。 非常感谢你提前。

编辑0:

persistence.xml文件似乎找到了正确的方言,但函数getTypeName(2003)在执行registerHibernateType(Types.ARRAY,“array”)之后抛出上述错误。 顺便说一句,我使用的是Hibernate 3.6.4.Final和postgresql 8.4-702.jdbc3。

编辑1:

我将以下行添加到PostgreSQLDialectArray构造函数中:

 registerColumnType(Types.ARRAY, "integer[$l]" ); 

这似乎部分解决了这个问题。 但是,现在我又收到了另一个错误:

 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 

显然,hibernate仍然不知道如何创建一个包含数组的表…

编辑2:

似乎postgresql不喜欢我的专栏被称为“数组”的事实。 我改变了它并且它起作用了。 该表由hibernate创建,带有一个整数数组。

但由于UserType实现中存在问题,我无法使用hibernate保存任何数组。 显然,使用createArrayOf创建数组失败。 我正在阅读有关这个​​问题的一些线程,告诉他们访问底层连接而不是包装器。 我想我将打开一个链接到这个的新线程,因为这个问题是完全不同的。

堆栈跟踪:

 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) 

编辑3:

最后,经过以下修改后,整数数组的UserType可以工作:

  • 在applicationContext.xml中添加以下行:


    ....

  • 从IntArrayUserType修改nullSafeSet

      @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); } 

但是从表blabla获取所有条目时仍然存在问题:函数findAllBlablas无法正常工作,只返回第一个条目…

编辑4:

事实上,它工作得很好但是eclipse控制台无法打印所有数据。 就这样!

 __________ HibernateSessionFactory.xml ___________________________    com.test.model.PostgreSQLDialectArray true true false 10 class create org.hibernate.cache.NoCacheProvider   ______________ PostgreSQLDialectArray.java _______________________ package com.test.model; import java.sql.Types; public class PostgreSQLDialectArray extends org.hibernate.dialect.PostgreSQL82Dialect{ public PostgreSQLDialectArray() { super(); System.out.println("Register Hibernate Type ... "); registerHibernateType(Types.ARRAY, "array"); System.out.println("Register Column Type ... "); registerColumnType(Types.ARRAY, "integer[]"); } } _____________ CustomArrayType.java ______________________________ package com.test.model; import java.io.Serializable; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.sql.Array; import org.hibernate.*; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.usertype.UserType; public class CustomArrayType 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, SessionImplementor session, 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, SessionImplementor session) 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 returnedClass() { return int[].class; } @Override public int[] sqlTypes() { return new int[] { SQLTYPE }; } } ____________ MyClass.java ___________________________ package com.test.model; import static javax.persistence.GenerationType.IDENTITY; import java.io.Serializable; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.Date; import java.util.List; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.Table; import org.hibernate.annotations.Type; @Entity @Table(name = "MyClass") public class MyClass implements Serializable { private static final long serialVersionUID = -2520209406925143850L; @Id @GeneratedValue(strategy = IDENTITY) private Long id; @Type(type = "com.test.model.CustomArrayType") @Column(name = "arrayDay", nullable = true) private int[] arrayDay; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public int[] getArrayDay() { return arrayDay; } public void setArrayDay(int[] arrayDay) { this.arrayDay = arrayDay; } } ______________ ArrayTypeTests.java ___________________________ package com.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.test.model.MyClass; import com.test.service.MyClassService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:application-context.xml" }) public class ArrayTypeTests implements ApplicationContextAware { @Autowired private MyClassService myClassService; public static ApplicationContext ctx; @Test public void test_() { System.out.println("Test junit"); } @Test public void test_arrayDays() { System.out.println("\n - Start test_array - "); MyClass myClass = myClassService.getMyClassById(1L); if (myClass != null) { int[] array = myClass.getArrayDay(); System.out.println("\n valor1:" + array[0]); System.out.println("\n valor2:" + array[1]); System.out.println("\n\n test_array OK "); } else { System.out.println("\n ERROR"); } System.out.println("\n - End test_array - "); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ctx = applicationContext; } } 

除了编辑3中的解决方案之外,当您不使用dbcp或c3p0之类的连接池时,它也非常有用。 – 您正在使用dbcp。

使用c3p0时可能的解决方案是使用proxool 。 有了这个,就不会有任何问题,因为proxool可以处理createArrayOf()

另一个是这样的:

  C3P0ProxyConnection connection = (C3P0ProxyConnection) _statement.getConnection(); Method method = Connection.class.getMethod("createArrayOf", new Class[] { String.class, Object[].class }); Array array = (Array) connection.rawConnectionOperation( method, C3P0ProxyConnection.RAW_CONNECTION, new Object[] {"integer", intArray}); _statement.setArray(_i, array); 

您需要从jdbc-class获取基础函数。 因此,使用getMethod获取它并将其与reflection一起使用。