说我有一个像这样的postgres表:
CREATE TABLE sal_emp (
name text,
pay_by_quarter integer[],
schedule text[][]
);
我是否能够使用Spring Data插入列pay_by_quarter
或schedule
?如果可能的话,它将如何看作存储库和实体?我还没有能够找到解决这个问题的任何文档或示例,可能是因为它与更常见的用例重叠,作为一对多关系插入到多个表中。说到这一点,我完全打算使用Postgresql array
数据类型而不使用关系表。
答案 0 :(得分:14)
您需要创建自己的类型并实施UserType interface
。基于下一个response,我已经编写了一个通用UserType
,可以在所有数组中使用它,但它必须使用非原始数据类型(Integer,Long,String ,. ..)。否则,请参阅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 };
}
}
然后,数组属性将是具有相同维度的相同类型的数据库:
integer[]
- &gt; Integer[]
text[][]
- &GT; String[][]
在这种特殊情况下,将GenericType
类置于属性
@Type(type = "packageofclass.GenericArrayUserType")
然后你的实体将是:
@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[][]
类型。你需要编写自己的类型,在你的情况下会有下一个:
整数[]
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 };
}
}
文本[] []
public class StringMultidimensionalArrayType 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 (String[][]) 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 String[0][];
}
Array array = resultSet.getArray(names[0]);
String[][] javaArray = (String[][]) 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 {
String[][] castObject = (String[][]) 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<String[][]> returnedClass() {
return String[][].class;
}
@Override
public int[] sqlTypes() {
return new int[] { Types.ARRAY };
}
}
在这种情况下,您的属性有不同的类型:
@Column(name="pay_by_quarter")
@Type(type = "packageofclass.IntArrayUserType")
private Integer[] payByQuarter;
@Column(name="schedule")
@Type(type = "packageofclass.StringMultidimensionalArrayType")
private String[][] schedule;
使用布尔值或布尔值似乎它不适用于GenericArrayUserType
,因此可以在类型为CREATE DDL
的{{1}}声明boolean
中创建解决方案:
bytea
你的财产没有任何类型:
CREATE TABLE sal_emp (
name text,
pay_by_quarter integer[],
schedule text[][],
wow_boolean bytea
);
在没有任何private boolean[][][] wowBoolean;
或Type
的情况下解析非常好。输出:Converter
使用wowBoolean=[[[true, false], [true, false]], [[true, true], [true, true]]])
@Converter
进行更新
我已尝试使用JPA 2.1
和@Converter
以及EclipseLink
的{{1}}选项。我刚试过Hibernate
(不是integer[]
)text[][]
就像这样(*我已将属性更改为Converter
但无关紧要):
List<Integer>
然后,将转换器添加到实体中的属性:
@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;
的解决方案不起作用。为什么? Hibernate不支持数据库数组(JPA specification
)....
然后我尝试使用EclipseLink(请参阅如何配置here)并且它可以工作,但并不总是......似乎有一个错误,它第一次运行良好,但接下来的时间不可能更新或查询此行。然后我只是成功添加新行但是......之后无法更新或查询。
目前,java.sql.Array
供应商似乎没有正确支持...只有JPA
Hibernate
的解决方案效果很好,但仅适用于UserType
。
答案 1 :(得分:0)
简单方法将
尝试将string []转换为字符串,然后在Entity类make中
@Column(name = "nice_work" columnDefinition="text")
将string []转换为字符串,反之亦然的功能
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;
}