4

I'm having trouble to create a JPA model class from which EclipseLink would create the following PostgreSQL DDL:

CREATE TABLE array_example (
  id serial not null, 
  params text[] not null
);

As I understand, PostgreSQL's array types are not SQL standard and thus not covered by the JPA standard. EclipseLink seems to have some kind of support for non-standard extensions though. So far I came up with the following class:

....
import org.eclipse.persistence.annotations.Array;
import org.eclipse.persistence.annotations.Struct;

@Entity
@Table(name = "array_example")
@Struct(name = "params") // Else: [EclipseLink-157] "Normal descriptors do not support non-relational extensions."
@XmlRootElement
public class ArrayExamplePostgres implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private int id;

    @NotNull
    @Array(databaseType = "TEXT[]") // Needs @Struct on class!
    @Column(name = "params")
    private List<String> params;
    ....

When running my test script I get the following error:

Mai 12, 2014 1:35:13 AM org.eclipse.persistence.default
WARNING: The default table generator currently only supports generating default table schema from a relational project.
Mai 12, 2014 1:35:13 AM org.eclipse.persistence.session.file:/srv/home/james/workspace/java_test/java_test_ee6_jpa/target/classes/_postgresJavaTestJpaPU
WARNING: 
Local Exception Stack: 
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: Unable to find server array type for provided name TEXT[].
Error Code: 0
Query: InsertObjectQuery(de.lathspell.java_test_ee6_jpa.model.ArrayExamplePostgres@3c77af84)
    at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:331)
    at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:326)
    at org.eclipse.persistence.mappings.structures.ObjectRelationalDataTypeDescriptor.buildFieldValueFromDirectValues(ObjectRelationalDataTypeDescriptor.java:102)
    at org.eclipse.persistence.mappings.foundation.AbstractCompositeDirectCollectionMapping.writeFromObjectIntoRow(AbstractCompositeDirectCollectionMapping.java:617)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildRow(ObjectBuilder.java:1394)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildRow(ObjectBuilder.java:1382)
    at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:450)
    at org.eclipse.persistence.queries.InsertObjectQuery.executeCommit(InsertObjectQuery.java:80)
    at org.eclipse.persistence.queries.InsertObjectQuery.executeCommitWithChangeSet(InsertObjectQuery.java:90)
    at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:300)
    at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:899)
    at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:798)
    at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
    at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2894)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1797)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1779)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1730)
    at org.eclipse.persistence.internal.sessions.CommitManager.commitNewObjectsForClassWithChangeSet(CommitManager.java:226)
    at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:125)
    at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4200)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1439)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithPreBuiltChangeSet(UnitOfWorkImpl.java:1585)
    at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.writeChanges(RepeatableWriteUnitOfWork.java:452)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:846)
    at de.lathspell.java_test_ee6_jpa.model.ArrayExamplePostgresTest.testArrays(ArrayExamplePostgresTest.java:31)
...

The PostgreSQL logfile contains two suspicious SELECT (that return 1009 for _text and then typedelim=NULL) shortly before it fails due to the missing table:

2014-05-12 01:34:32 CEST postgres@java_test_ee6_jpa [17555] LOG:  execute <unnamed>: SELECT oid FROM pg_catalog.pg_type WHERE typname = $1
2014-05-12 01:34:32 CEST postgres@java_test_ee6_jpa [17555] DETAIL:  parameters: $1 = '_text

2014-05-12 01:34:32 CEST postgres@java_test_ee6_jpa [17555] LOG:  execute <unnamed>: SELECT e.typdelim FROM pg_catalog.pg_type t, pg_catalog.pg_type e WHERE t.oid = $1 and t.typelem = e.oid
2014-05-12 01:34:32 CEST postgres@java_test_ee6_jpa [17555] DETAIL:  parameters: $1 = '1009'

2014-05-12 01:34:32 CEST postgres@java_test_ee6_jpa [17555] LOG:  execute S_1: BEGIN
2014-05-12 01:34:32 CEST postgres@java_test_ee6_jpa [17555] ERROR:  relation "array_example" does not exist at character 13
2014-05-12 01:34:32 CEST postgres@java_test_ee6_jpa [17555] STATEMENT:  INSERT INTO array_example (params) VALUES ($1)

The relevant part ofthe persistence config is:

<persistence-unit name="postgresJavaTestJpaPU" transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <class>de.lathspell.java_test_ee6_jpa.model.ArrayExamplePostgres</class>
    ...
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
        <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
        <property name="javax.persistence.jdbc.url" value="jdbc:postgresql:java_test_ee6_jpa"/>
        <property name="javax.persistence.jdbc.user" value="postgres"/>
        <property name="javax.persistence.jdbc.password" value="secret"/>
        <property name="eclipselink.ddl-generation.output-mode" value="both"/>
        <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
        <property name="eclipselink.drop-ddl-jdbc-file-name" value="src/main/sql/eclipselink-postgres-drop.sql"/>
        <property name="eclipselink.create-ddl-jdbc-file-name" value="src/main/sql/eclipselink-postgres-create.sql"/>
        <property name="eclipselink.jdbc.native-sql" value="true"/>
    </properties>
</persistence-unit>

(The complete class is at https://svn.code.sf.net/p/lathspellsphp/code/java_test/java_test_ee7_jpa/src/main/java/de/lathspell/java_test_ee7_jpa/model/ArrayExamplePostgres.java)

EDIT:

When using @Array(databaseType="TEXT") I get one SQL query but no schema generated:

2014-05-12 23:03:08 CEST postgres@java_test_ee7_jpa [4183] LOG:  execute <unnamed>: SELECT pg_type.oid   FROM pg_catalog.pg_type   LEFT   JOIN (select ns.oid as nspoid, ns.nspname, r.r           from pg_namespace as ns           join ( select s.r, (current_schemas(false))[s.r] as nspname                    from generate_series(1, array_upper(current_schemas(false), 1)) as s(r) ) as r          using ( nspname )        ) as sp     ON sp.nspoid = typnamespace  WHERE typname = $1  ORDER BY sp.r, pg_type.oid DESC LIMIT 1
2014-05-12 23:03:08 CEST postgres@java_test_ee7_jpa [4183] DETAIL:  parameters: $1 = '_TEXT'
-> 0 rows

When using @Array(databaseType="text[]" I get one SQL query but no schema generated:

2014-05-12 23:12:21 CEST postgres@java_test_ee7_jpa [4435] LOG:  execute <unnamed>: SELECT pg_type.oid   FROM pg_catalog.pg_type   LEFT   JOIN (select ns.oid as nspoid, ns.nspname, r.r           from pg_namespace as ns           join ( select s.r, (current_schemas(false))[s.r] as nspname                    from generate_series(1, array_upper(current_schemas(false), 1)) as s(r) ) as r          using ( nspname )        ) as sp     ON sp.nspoid = typnamespace  WHERE typname = $1  ORDER BY sp.r, pg_type.oid DESC LIMIT 1
2014-05-12 23:12:21 CEST postgres@java_test_ee7_jpa [4435] DETAIL:  parameters: $1 = '_text[]'
-> 0 rows

When using @Array(databaseType="text" I get two SQL queries that actually produce results but still no table is created nor is the schema.sql created by EclipseLink:

2014-05-12 23:04:46 CEST postgres@java_test_ee7_jpa [4266] LOG:  execute <unnamed>: SELECT pg_type.oid   FROM pg_catalog.pg_type   LEFT   JOIN (select ns.oid as nspoid, ns.nspname, r.r           from pg_namespace as ns           join ( select s.r, (current_schemas(false))[s.r] as nspname                    from generate_series(1, array_upper(current_schemas(false), 1)) as s(r) ) as r          using ( nspname )        ) as sp     ON sp.nspoid = typnamespace  WHERE typname = $1  ORDER BY sp.r, pg_type.oid DESC LIMIT 1
2014-05-12 23:04:46 CEST postgres@java_test_ee7_jpa [4266] DETAIL:  parameters: $1 = '_text'
--> 1009
2014-05-12 23:04:46 CEST postgres@java_test_ee7_jpa [4266] LOG:  execute <unnamed>: SELECT e.typdelim FROM pg_catalog.pg_type t, pg_catalog.pg_type e WHERE t.oid = $1 and t.typelem = e.oid
2014-05-12 23:04:46 CEST postgres@java_test_ee7_jpa [4266] DETAIL:  parameters: $1 = '1009'
--> ','

At the last example a new warning is printed. It vanishes if I remove the ArrayExample model class from the persistence.xml so it seems relevant:

org.eclipse.persistence.default
WARNING: The default table generator currently only supports generating default table schema from a relational project.
3
  • I suggested in a deleted answer that it seems to be looking for the array type of text[], ie array of array of text, which isn't supported, so you should try just text. That seems to have resolved the immediate issue but created different ones per the edits above. Commented May 13, 2014 at 0:27
  • I'm afraid lots of the Java ORMs aren't good at anything but the most basic lowest-common-denominator features of relational DBs. In this case I think it's trying to generate the field as if it was a non-relational object for EclipseLink's "NoSQL" support. The last query certainly found the correct oid (1009) for the built-in array of text type. Commented May 13, 2014 at 0:29
  • Schema generation, as the warning states, is only supported with relational DB classes. As soon as you add the Struct or Array extension types, you can't use schema generation, as support for all the various DB types is just not there, and really, all that usable anyway - these types are meant for custom Database types on legacy systems. You are in most cases better off using an existing and portable type, or using a script to create your database. Commented Apr 9, 2019 at 16:14

2 Answers 2

1

Maybe this sample-project is helpful:

https://github.com/phstudy/jpa-array-converter-sample

Sign up to request clarification or add additional context in comments.

Comments

0

try with keeping the @Struct(name = "params") Annotation over the class and edit the following:

   @Array(databaseType="text")
   @Column(name = "params, columnDefinition ="text array")
   private List<String> params;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.