独自のタイプを作成し、UserType interface
を実装する必要があります。次にresponseに基づいて、私はすべての配列で使用する汎用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 };
}
}
そして配列プロパティは、同じ寸法のデータベースの同じタイプであろう:
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;
ブールまたはブール値で更新休止のUserType
それはGenericArrayUserType
で動作しないので、解決策はタイプbytea
のごCREATE DDL
宣言boolean
に作成することができそうです:
CREATE TABLE sal_emp (
name text,
pay_by_quarter integer[],
schedule text[][],
wow_boolean bytea
);
そして、あなたのタイプなしのプロパティ:
private boolean[][][] wowBoolean;
Type
またはConverter
がなくても非常に良いです。出力:JPA 2.1
の@Converter
でwowBoolean=[[[true, false], [true, false]], [[true, true], [true, true]]])
アップデート私はEclipseLink
とHibernate
でJPA 2.1の@Converter
とオプションを試してみました。私はちょうどこの(* IがList<Integer>
にプロパティを変更したが、それは問題ではありません)のようinteger[]
(ないtext[][]
)Converter
を試してみた:
@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;
したがって、JPA specification
に基づくソリューションは機能しません。どうして? Hibernateは
その後私はのEclipseLink(hereを設定する方法を参照)を試したし、それが動作しますが、常にではない...バグがありますようだ、それが動作する....(java.sql.Array
)を、データベースの配列をサポートしていません。最初はうまくいきますが、次回はこの行を更新またはクエリできません。ちょうど私が新しい行を追加し、成功しましたが、それは後に更新またはクエリすることはできません....
結論
現時点ではその後、適切にJPA
ベンダーがそこにサポートされていないようです... Hibernate
UserType
のソリューションだけがうまくいきますが、それはちょうどHibernate
です。
はい、これはSpringデータで実現できます。しかし、これは1NF(https://en.wikipedia.org/wiki/First_normal_form)に違反する非常に悪い習慣であると私は示唆しています。可能であれば、モデルリビジョンを検討してください。 – crm86
@ crm86私が保存している特定のデータ(Twitterからのつぶやきの大量ストリーム)に対してFNFを持たない方がよいです。私は1対多の関係を1つのテーブルに保存したい(そのように価値のないデータのために非常に多くの結合を避けるため)。私はまた、NoSQLドキュメントデータベースが私のために維持するのが難しいので避けたい。私は、 'array'データ型の別の代替手段が' JSONB'であり、すべてをpostgresqlの文書として保存すると思います。しかし、私は列(およびいくつかの配列のデータ型)を扱う方がJSON(PostgreSQL)を使うよりも優れていると思います。 – Zombies