Вам нужно создать свой собственный тип и реализовать UserType interface
. Основываясь на следующем ответе, я написал универсальный 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[]
-> Integer[]
text[][]
-> 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;
Обновить тип пользователя Hibernate
С логическим или логическим значением кажется, что это не работает с GenericArrayUserType
, поэтому решения могут быть созданы в вашем CREATE DDL
объявлении 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
из JPA 2.1 с EclipseLink
и 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;
}
}
Затем добавьте преобразователь к свойству в Entity:
@Convert(converter=ConverterListInteger.class)
private List<Integer> pay_by_quarter;
Так что решение на основе JPA specification
не работает. Почему? Hibernate не поддерживает массивы баз данных (java.sql.Array
)....
Затем я попытался использовать EclipseLink (см., как настроить здесь) и это работает, но не всегда... Кажется, это ошибка. В первый раз это работает хорошо, но в следующий раз невозможно обновить или запросить эту строку. Затем мне удалось добавить новые строки, но после этого невозможно обновить или запросить....
Заключение
На данный момент кажется, что JPA
производители не поддерживают должным образом... Хорошо работает только решение с Hibernate
UserType
, но это только для Hibernate
.
person
Pau
schedule
26.08.2016
array
будетJSONB
, и все будет храниться как документ в postgresql. Но я думаю, что работать со столбцами (и несколькими типами данных массива) лучше, чем с JSON (в Postgresql). - person Zombies   schedule 24.08.2016