Java 如何使用SpringData/JPA插入到Postgres数组类型列中?
假设我有一张postgres表,如下所示:Java 如何使用SpringData/JPA插入到Postgres数组类型列中?,java,postgresql,spring-data,spring-data-jpa,Java,Postgresql,Spring Data,Spring Data Jpa,假设我有一张postgres表,如下所示: CREATE TABLE sal_emp ( name text, pay_by_quarter integer[], schedule text[][] ); 我甚至可以使用Spring数据插入列pay\u by\u quarty或schedule?如果可能的话,它作为一个存储库和实体会是什么样子?我还没有找到任何文档或示例来解决这个问题,可能是因为它与更常见的用例重叠,以一对多关系插入
CREATE TABLE sal_emp (
name text,
pay_by_quarter integer[],
schedule text[][]
);
我甚至可以使用Spring数据插入列
pay\u by\u quarty
或schedule
?如果可能的话,它作为一个存储库和实体会是什么样子?我还没有找到任何文档或示例来解决这个问题,可能是因为它与更常见的用例重叠,以一对多关系插入多个表中。说到这里,我完全打算使用Postgresql数组
数据类型,而不使用关系表。您需要创建自己的类型并实现用户类型接口
。基于next,我编写了一个通用的UserType
,用于所有数组,它可以工作,但必须使用非基本数据类型(整型、长型、字符串等)。否则,请参见上面使用Boolean
类型的更新
public class GenericArrayUserType<T extends Serializable> implements UserType {
protected static final int[] SQL_TYPES = { Types.ARRAY };
private Class<T> typeParameterClass;
@Override
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return this.deepCopy(cached);
}
@Override
public Object deepCopy(Object value) throws HibernateException {
return value;
}
@SuppressWarnings("unchecked")
@Override
public Serializable disassemble(Object value) throws HibernateException {
return (T) this.deepCopy(value);
}
@Override
public boolean equals(Object x, Object y) throws HibernateException {
if (x == null) {
return y == null;
}
return x.equals(y);
}
@Override
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
@Override
public boolean isMutable() {
return true;
}
@Override
public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
if (resultSet.wasNull()) {
return null;
}
if (resultSet.getArray(names[0]) == null) {
return new Integer[0];
}
Array array = resultSet.getArray(names[0]);
@SuppressWarnings("unchecked")
T javaArray = (T) array.getArray();
return javaArray;
}
@Override
public void nullSafeSet(PreparedStatement statement, Object value, int index, SessionImplementor session)
throws HibernateException, SQLException {
Connection connection = statement.getConnection();
if (value == null) {
statement.setNull(index, SQL_TYPES[0]);
} else {
@SuppressWarnings("unchecked")
T castObject = (T) value;
Array array = connection.createArrayOf("integer", (Object[]) castObject);
statement.setArray(index, array);
}
}
@Override
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}
@Override
public Class<T> returnedClass() {
return typeParameterClass;
}
@Override
public int[] sqlTypes() {
return new int[] { Types.ARRAY };
}
}
那么您的实体将是:
@Entity
@Table(name="sal_emp")
public class SalEmp {
@Id
private String name;
@Column(name="pay_by_quarter")
@Type(type = "packageofclass.GenericArrayUserType")
private Integer[] payByQuarter;
@Column(name="schedule")
@Type(type = "packageofclass.GenericArrayUserType")
private String[][] schedule;
//Getters, Setters, ToString, equals, and so on
}
如果您不想使用此通用的UserType
Integer[]类型并写入String[]][]
类型。您需要编写自己的类型,在您的情况下,下一步是:
- 整数[]
更新Hibernate用户类型 对于布尔型或布尔型,它似乎不适用于public class IntArrayUserType implements UserType { protected static final int[] SQL_TYPES = { Types.ARRAY }; @Override public Object assemble(Serializable cached, Object owner) throws HibernateException { return this.deepCopy(cached); } @Override public Object deepCopy(Object value) throws HibernateException { return value; } @Override public Serializable disassemble(Object value) throws HibernateException { return (Integer[]) this.deepCopy(value); } @Override public boolean equals(Object x, Object y) throws HibernateException { if (x == null) { return y == null; } return x.equals(y); } @Override public int hashCode(Object x) throws HibernateException { return x.hashCode(); } @Override public boolean isMutable() { return true; } @Override public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException { if (resultSet.wasNull()) { return null; } if (resultSet.getArray(names[0]) == null) { return new Integer[0]; } Array array = resultSet.getArray(names[0]); Integer[] javaArray = (Integer[]) array.getArray(); return javaArray; } @Override public void nullSafeSet(PreparedStatement statement, Object value, int index, SessionImplementor session) throws HibernateException, SQLException { Connection connection = statement.getConnection(); if (value == null) { statement.setNull(index, SQL_TYPES[0]); } else { Integer[] castObject = (Integer[]) value; Array array = connection.createArrayOf("integer", castObject); statement.setArray(index, array); } } @Override public Object replace(Object original, Object target, Object owner) throws HibernateException { return original; } @Override public Class<Integer[]> returnedClass() { return Integer[].class; } @Override public int[] sqlTypes() { return new int[] { Types.ARRAY }; } }
,因此可以在GenericArrayUserType
declarecreate DDL
类型的Boolean
中创建解决方案:bytea
以及您的财产(不含任何类型):CREATE TABLE sal_emp ( name text, pay_by_quarter integer[], schedule text[][], wow_boolean bytea );
private boolean[]wowBoolean代码> 它的解析非常好,没有任何
或类型
。输出:转换器
使用wowBoolean=[[[true,false],[true,false]],[[true,true],[true,true]]])
我用JPA2.1的JPA 2.1的
更新@Converter
和@Converter
和eclipseelink
尝试了一个选项。我刚刚尝试过这样的Hibernate
(而不是integer[]
)text[][]
(*我已将属性更改为Converter
),但这并不重要):列表
然后,将转换器添加到实体中的属性:@转换器 公共类ConverterListenter实现AttributeConverter{ @凌驾 公共数组convertToDatabaseColumn(列表属性){ DataSource source=ApplicationContextHolder.getContext().getBean(DataSource.class); 试一试{ 连接conn=source.getConnection(); Array Array=conn.createArrayOf(“integer”,attribute.toArray()); 返回数组; }捕获(SQLE异常){ e、 printStackTrace(); } 返回null; } @凌驾 公共列表convertToEntityAttribute(数组dbData){ 列表=新的ArrayList(); 试一试{ 对于(对象对象:(对象[])dbData.getArray()){ 添加((整数)对象); } }捕获(SQLE异常){ e、 printStackTrace(); } 退货清单; } }
@Convert(converter=ConverterListInteger.class) private List<Integer> pay_by_quarter;
因此,基于@Convert(converter=converterlistineger.class) 私人名单按季度支付;
JPA规范的解决方案不起作用。为什么?Hibernate不支持数据库数组(
) 然后我尝试了EclipseLink(请参阅如何配置),它可以工作,但并不总是……似乎有一个bug,它第一次工作得很好,但下一次无法更新或查询此行。然后我成功地添加了新行,但之后无法更新或查询 结论java.sql.Array
目前,
供应商似乎不支持。。。只有使用JPA
Hibernate
的解决方案才能很好地工作,但它仅适用于UserType
Hibernate简单的方法将是 尝试将字符串[]转换为字符串,然后在实体类中生成
函数,用于将字符串[]转换为字符串,反之亦然@Column(name=“nice\u work”columnDefinition=“text”)
private static String stringArrayTOString(String[] input) { StringBuffer sb =new StringBuffer(""); int i=0; for(String value:input) { if(i!=0) { sb.append(","); } sb.append(value); i++; } return sb.toString(); } private static String[] stringToStringArray(String input) { String[] output = input.split(","); return output; }
是的,您可以通过Spring数据实现这一点。但是,我认为这是一种非常糟糕的做法,违反了1NF()。如果可能的话,考虑模型修订。@ CRM86-我宁愿没有FNF用于特定的数据我正在保存(从推特推特的大量流)。我希望将一对多关系存储在一个表中(以避免对这些低值数据进行如此多的联接)。我还想避免使用NoSQL文档数据库,因为它们难以维护。我假设
数据类型的另一种替代方法是array
,并将所有内容作为文档存储在postgresql中。但是我认为使用列(和一些数组数据类型)比使用JSON(在Postgresql中)要好。谢谢。有没有不依赖Hibernate的方法来做到这一点??例如,是否可以不使用JSONB
??或者依赖Hibernate是这个用例的唯一解决方案吗?这个解决方案依赖于org.hibernate.usertype.usertype
,我知道,但它认为更简单。如果您不想依赖“Hibernate”,可以使用JPA2.1的Hibernate
。请参阅本文:在处理JPA2.1的示例中,您使用了@Converter
类型,即Array
对吗?您从未在第一个代码块中初始化过typeParameterClass。您没有得到空指针异常吗?我使用GenericarArrayUserType得到以下错误:“没有JDBC类型的方言映射:2003”java.sql.Array
CREATE TABLE sal_emp ( name text, pay_by_quarter integer[], schedule text[][], wow_boolean bytea );
@Converter public class ConverterListInteger implements AttributeConverter<List<Integer>, Array>{ @Override public Array convertToDatabaseColumn(List<Integer> attribute) { DataSource source = ApplicationContextHolder.getContext().getBean(DataSource.class); try { Connection conn = source.getConnection(); Array array = conn.createArrayOf("integer", attribute.toArray()); return array; } catch (SQLException e) { e.printStackTrace(); } return null; } @Override public List<Integer> convertToEntityAttribute(Array dbData) { List<Integer> list = new ArrayList<>(); try { for(Object object : (Object[]) dbData.getArray()){ list.add((Integer) object); } } catch (SQLException e) { e.printStackTrace(); } return list; } }
@Convert(converter=ConverterListInteger.class) private List<Integer> pay_by_quarter;
private static String stringArrayTOString(String[] input) { StringBuffer sb =new StringBuffer(""); int i=0; for(String value:input) { if(i!=0) { sb.append(","); } sb.append(value); i++; } return sb.toString(); } private static String[] stringToStringArray(String input) { String[] output = input.split(","); return output; }