heeft iemand met succes een numerieke array in PostgreSQL toegewezen aan een numerieke array in Java via Hibernate?
sql:
CREATE TABLE sal_emp (name text, pay_by_quarter integer[]);
INSERT INTO sal_emp VALUES ('one', '{1,2,3}');
INSERT INTO sal_emp VALUES ('two', '{4,5,6}');
INSERT INTO sal_emp VALUES ('three', '{2,4,6}');
toewijzing:
<hibernate-mapping>
<class name="SalEmp" table="sal_emp">
<id name="name" />
<property name="payByQuarter" column="pay_by_quarter" />
</class>
</hibernate-mapping>
klas:
public class SalEmp implements Serializable{
private String name;
private Integer[] payByQuarter;
...// getters & setters
}
ik krijg een uitzondering bij het opvragen van de tabel.
Antwoord 1, autoriteit 100%
Maven-afhankelijkheid
Het eerste dat u moet doen, is de volgende Hibernate TypesMaven-afhankelijkheid in uw project pom.xml
configuratiebestand:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>${hibernate-types.version}</version>
</dependency>
Ervan uitgaande dat u deze tabel in uw database heeft:
create table event (
id int8 not null,
version int4,
sensor_names text[],
sensor_values integer[],
primary key (id)
)
En je wilt het als volgt in kaart brengen:
@Entity(name = "Event")
@Table(name = "event")
@TypeDefs({
@TypeDef(
name = "string-array",
typeClass = StringArrayType.class
),
@TypeDef(
name = "int-array",
typeClass = IntArrayType.class
)
})
public static class Event extends BaseEntity {
@Type( type = "string-array" )
@Column(
name = "sensor_names",
columnDefinition = "text[]"
)
private String[] sensorNames;
@Type( type = "int-array" )
@Column(
name = "sensor_values",
columnDefinition = "integer[]"
)
private int[] sensorValues;
//Getters and setters omitted for brevity
}
De string-array
en int-array
zijn aangepaste typen die kunnen worden gedefinieerd in de superklasse BaseEntity
:
@TypeDefs({
@TypeDef(
name = "string-array",
typeClass = StringArrayType.class
),
@TypeDef(
name = "int-array",
typeClass = IntArrayType.class
)
})
@MappedSuperclass
public class BaseEntity {
@Id
private Long id;
@Version
private Integer version;
//Getters and setters omitted for brevity
}
De StringArrayType
en IntArrayType
zijn klassen die worden aangeboden door het Hibernate Types-project.
Testtijd
Als je nu een aantal entiteiten invoegt;
Event nullEvent = new Event();
nullEvent.setId(0L);
entityManager.persist(nullEvent);
Event event = new Event();
event.setId(1L);
event.setSensorNames(
new String[] {
"Temperature",
"Pressure"
}
);
event.setSensorValues(
new int[] {
12,
756
}
);
entityManager.persist(event);
Hibernate gaat de volgende SQL-statements genereren:
INSERT INTO event (
version,
sensor_names,
sensor_values,
id
)
VALUES (
0,
NULL(ARRAY),
NULL(ARRAY),
0
)
INSERT INTO event (
version,
sensor_names,
sensor_values,
id
)
VALUES (
0,
{"Temperature","Pressure"},
{"12","756"},
1
)
Antwoord 2, autoriteit 65%
Hibernate ondersteunt standaard geen database-arrays (bijv. die zijn toegewezen aan java.sql.Array
).
De typen
array
en primitive-array
die door Hibernate worden geleverd, zijn bedoeld voor het in kaart brengen van Java-arrays in een backing-tabel – ze zijn in feite een variatie op een-op-veel/verzameling- of-elements mappings, dus dat is niet wat je wilt.
De nieuwste PostgreSQL JDBC-driver (8.4.whatever) ondersteunt JDBC4 Connection.createArrayOf()
methode en ResultSet.getArray()
en PreparedStatement.setArray()methoden, zodat u uw eigen UserType
kunt schrijven om array-ondersteuning te bieden.
Hieris een gebruikerstype implementatie met Oracle-array die een goed startpunt biedt, het is redelijk eenvoudig om het aan te passen om in plaats daarvan java.sql.Array
te verwerken.
Antwoord 3, autoriteit 16%
Misschien is dit nuttig voor iemand anders: ik ontdekte dat het in mijn geval slecht presteert en niet kan worden gebruikt met c3p0. (Alleen deze problemen kort onderzocht, kunnen ze worden opgelost, corrigeer me alstublieft!)
Sluimerstand 3.6:
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 java.util.Arrays;
import org.apache.commons.lang.ArrayUtils;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
public class IntArrayUserType implements UserType {
protected static final int SQLTYPE = java.sql.Types.ARRAY;
@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 ArrayUtils.toPrimitive(javaArray);
}
@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 = ArrayUtils.toObject(castObject);
Array array = connection.createArrayOf("integer", integers);
statement.setArray(i, array);
}
@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<int[]> returnedClass() {
return int[].class;
}
@Override
public int[] sqlTypes() {
return new int[] { SQLTYPE };
}
}
Antwoord 4, autoriteit 16%
Dit is getest met stringarrays. Misschien zijn er enkele aanpassingen in de converter nodig voor numerieke arrays. Dit werkt met Spring JPA.
1) voeg PostgreSQLTextArray
toe aan uw project
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Map;
/**
* This is class provides {@link java.sql.Array} interface for PostgreSQL <code>text</code> array.
*
* @author Valentine Gogichashvili
*
*/
public class PostgreSQLTextArray implements java.sql.Array {
private final String[] stringArray;
private final String stringValue;
/**
* Initializing constructor
* @param stringArray
*/
public PostgreSQLTextArray(String[] stringArray) {
this.stringArray = stringArray;
this.stringValue = stringArrayToPostgreSQLTextArray(this.stringArray);
}
@Override
public String toString() {
return stringValue;
}
private static final String NULL = "NULL";
/**
* This static method can be used to convert an string array to string representation of PostgreSQL text array.
* @param a source String array
* @return string representation of a given text array
*/
public static String stringArrayToPostgreSQLTextArray(String[] stringArray) {
final int arrayLength;
if ( stringArray == null ) {
return NULL;
} else if ( ( arrayLength = stringArray.length ) == 0 ) {
return "{}";
}
// count the string length and if need to quote
int neededBufferLentgh = 2; // count the beginning '{' and the ending '}' brackets
boolean[] shouldQuoteArray = new boolean[stringArray.length];
for (int si = 0; si < arrayLength; si++) {
// count the comma after the first element
if ( si > 0 ) neededBufferLentgh++;
boolean shouldQuote;
final String s = stringArray[si];
if ( s == null ) {
neededBufferLentgh += 4;
shouldQuote = false;
} else {
final int l = s.length();
neededBufferLentgh += l;
if ( l == 0 || s.equalsIgnoreCase(NULL) ) {
shouldQuote = true;
} else {
shouldQuote = false;
// scan for commas and quotes
for (int i = 0; i < l; i++) {
final char ch = s.charAt(i);
switch(ch) {
case '"':
case '\\':
shouldQuote = true;
// we will escape these characters
neededBufferLentgh++;
break;
case ',':
case '\'':
case '{':
case '}':
shouldQuote = true;
break;
default:
if ( Character.isWhitespace(ch) ) {
shouldQuote = true;
}
break;
}
}
}
// count the quotes
if ( shouldQuote ) neededBufferLentgh += 2;
}
shouldQuoteArray[si] = shouldQuote;
}
// construct the String
final StringBuilder sb = new StringBuilder(neededBufferLentgh);
sb.append('{');
for (int si = 0; si < arrayLength; si++) {
final String s = stringArray[si];
if ( si > 0 ) sb.append(',');
if ( s == null ) {
sb.append(NULL);
} else {
final boolean shouldQuote = shouldQuoteArray[si];
if ( shouldQuote ) sb.append('"');
for (int i = 0, l = s.length(); i < l; i++) {
final char ch = s.charAt(i);
if ( ch == '"' || ch == '\\' ) sb.append('\\');
sb.append(ch);
}
if ( shouldQuote ) sb.append('"');
}
}
sb.append('}');
assert sb.length() == neededBufferLentgh;
return sb.toString();
}
@Override
public Object getArray() throws SQLException {
return stringArray == null ? null : Arrays.copyOf(stringArray, stringArray.length);
}
@Override
public Object getArray(Map<String, Class<?>> map) throws SQLException {
return getArray();
}
@Override
public Object getArray(long index, int count) throws SQLException {
return stringArray == null ? null : Arrays.copyOfRange(stringArray, (int) index, (int) index + count);
}
@Override
public Object getArray(long index, int count, Map<String, Class<?>> map) throws SQLException {
return getArray(index, count);
}
@Override
public int getBaseType() throws SQLException {
return java.sql.Types.VARCHAR;
}
@Override
public String getBaseTypeName() throws SQLException {
return "text";
}
@Override
public ResultSet getResultSet() throws SQLException {
throw new UnsupportedOperationException();
}
@Override
public ResultSet getResultSet(Map<String, Class<?>> map) throws SQLException {
throw new UnsupportedOperationException();
}
@Override
public ResultSet getResultSet(long index, int count) throws SQLException {
throw new UnsupportedOperationException();
}
@Override
public ResultSet getResultSet(long index, int count, Map<String, Class<?>> map) throws SQLException {
throw new UnsupportedOperationException();
}
@Override
public void free() throws SQLException {
}
}
2) Voeg ListToArrayConverter
toe aan uw code
import org.postgresql.jdbc4.Jdbc4Array;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@Converter(autoApply = true)
public class ListToArrayConveter implements AttributeConverter<List<String>, Object> {
@Override
public PostgreSQLTextArray convertToDatabaseColumn(List<String> attribute) {
if (attribute == null || attribute.isEmpty()) {
return null;
}
String[] rst = new String[attribute.size()];
return new PostgreSQLTextArray(attribute.toArray(rst));
}
@Override
public List<String> convertToEntityAttribute(Object dbData) {
List<String> rst = new ArrayList<>();
try {
String[] elements = (String[]) ((Jdbc4Array) dbData).getArray();
for (String element : elements) {
rst.add(element);
}
} catch (SQLException e) {
e.printStackTrace();
}
return rst;
}
}
3) Gebruik het!
@Entity
@Table(name = "emails")
public class Email {
[...]
@SuppressWarnings("JpaAttributeTypeInspection")
@Column(name = "subject", columnDefinition = "text[]")
@Convert(converter = ListToArrayConveter.class)
private List<String> subject;
[...]
Antwoord 5, autoriteit 12%
Ik heb een String[]
kunnen opslaan in PostgreSQL 9.4 en EclipseLink 2.6.2 via de JPA Converter-aanpak die op hier
wat de bron lijkt te zijn voor het antwoord van
Tk421 van 1 juli 2016.
Het laden van een array vanuit DB werkt ook goed.
Aanvullend toegevoegd aan persistence.xml
mijn pad naar ListToArrayConverter:
<class> com.foo1.foo2.foo3.backend.jpa.convert.ListToArrayConverter </class>
Vermeld dat Jdbc4Array
niet meer aanwezig is in het Postgre JDBC-stuurprogramma, gebruik in plaats daarvan:
org.postgresql.jdbc.PgArray
Zie hier:
pakket org.postgresql.jdbc4 ontbreekt sinds 9.4-1207
Antwoord 6, Autoriteit 8%
Hier is de int[]
UNERTYTYPE. Ik draaide altijd wat je hebt waarna je de null-cheques voor nullSafeGet()
en nullSafeSet()
:
import org.apache.commons.lang.ArrayUtils;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;
import java.io.Serializable;
import java.sql.Array;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class IntegerArrayUserType implements UserType {
protected static final int SQLTYPE = java.sql.Types.ARRAY;
@Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {
Array array = rs.getArray(names[0]);
if (array == null) {
return null;
}
Integer[] javaArray = (Integer[]) array.getArray();
return ArrayUtils.toPrimitive(javaArray);
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {
Connection connection = st.getConnection();
if (value == null) {
st.setNull( index, sqlTypes()[0] );
} else {
int[] castObject = (int[]) value;
Integer[] integers = ArrayUtils.toObject(castObject);
Array array = connection.createArrayOf("integer", integers);
st.setArray(index, array);
}
}
@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<int[]> returnedClass() {
return int[].class;
}
@Override
public int[] sqlTypes() {
return new int[] { SQLTYPE };
}
}