0

I'm testing my JPA classes using Arquillian against "remote" containers (Glassfish 4 and WildFly 10, to be specific). I want to:

  • Generate the database schema from the JPA entities' definitions (as opposed to SQL scripts).
  • Pre-load some data to imitate "the data that was already there". For a most trivial example, to test if I can remove an entity without creating it in the same test first.
  • Be able to perform queries, inserts, updates, deletes, etc.

There are javax.persistence.* properties that seem to fit the bill, so one might assume, that the following will work:

<persistence-unit name="test">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <jta-data-source>java:/H2DS</jta-data-source>
        <properties>

      <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>

      <property name="javax.persistence.schema-generation.create-source" value="metadata"/>


      <property name="javax.persistence.sql-load-script-source" value="META-INF/sql/load_script.sql"/>

      <property name="javax.persistence.schema-generation.drop-source" value="script"/>
      <property name="javax.persistence.schema-generation.drop-script-source" value="META-INF/sql/drop.sql" />          

</properties>

Well, not quite.

If one uses a GenerationType.TABLE, then one must update the values for the primary key sequences along with inserting new entities using an SQL script. The problem is that though a few entities are inserted using script (the primary key value in "generator-table" is also updated, of course), the JPA provider does not update its preallocated IDs in any way. So, what happens, as much as I can judge from the logs, is:

  • the "generator-table" is created
  • the primary keys 1-5 are pre-allocated (but the value in DB table is not updated
  • Three entries from script are inserted (but, again, this is not committed yet)
  • select and update statements for "generator-table" are prepared (but not executed yet)
  • insert statement for an entity instance from the test is prepared, and a wrong primary key is assigned. "Wrong" is -1 in case of Hibernate, and 1 in case of EclipseLink. The latter throws exception because of duplicate entry, of course.

Solutions found so far

EclipseLink: works, if both DB schema and data pre-loading are done with scripts. Suboptimal for the development stage, as one has to fiddle with SQL scripts often.

Hibernate: works, if the following implementation-specific option is set:

<property name="hibernate.id.new_generator_mappings" value="false"/>

That allows to persist an entity from test, the primary key being 3 times the allocation size (15, with 3 entities).

Now, to the question: is there some "standard JPA", implementation-independent way to achieve my requirements as listed in the beginning of this question? They do not seem extravagant at all, so there should be existing solutions, right?

badbishop
  • 1,281
  • 2
  • 18
  • 38
  • How do you want to populate data if not by script? None of the providers are going to modify your population scripts based on model data changes, so it isn't clear what you are looking for that you don't already have. Are you looking for a JPA callback method that you can use to populate entities? If so, you are better off using your test framework. – Chris Nov 07 '17 at 17:32
  • @Chris: to put it short, I'm looking for a way to pre-populate the data for my tests by JPA means. Without delving too deep into peculiarities of a particular JPA implementation. Populating data with script specified in persistence.xml doesn't work cleanly/conveniently enough in my humble opinion. See my question. – badbishop Nov 08 '17 at 06:47
  • I saw the question - what isn't clear is why you aren't using your test frameworks setup methods with JPA to populate using entities? Any changes to your model has to be reflected in the tests and the population data, so this would give you indications that you missed something at build time, rather than when running scripts. There is no tool I'm aware of that can anticipate what data you need for testing - your tests have to do that themselves. I'm not sure what I'm missing. You know JPA can create the tables from the mode rather than using scripts? – Chris Nov 08 '17 at 15:34
  • @Chris: because this would taint the test results. What I need to imitate is: 1) some persisted data exists 2) HTTP request comes 3) some operations are performed on existing data. I "know JPA can create the tables from the mode rather than using scripts". The problem is, EclipseLink creates the schema from Java classes, loads the data from script, and then pretends it DIDN'T load the data from script, ignoring the primary keys allocated for the entities from the script. So, with EclipseLink, it seems that either both schema and data come from scripts, or neither of them. – badbishop Nov 09 '17 at 08:22
  • "the primary keys 1-5 are pre-allocated (but the value in DB table is not updated" - how can any provider know about these entries if you don't maintain the sequence used in the database? If you insert 5 objects outside of JPA (via script or other application), you must increment the sequence yourself. Otherwise, you can change your sequence definition initialValue to be higher than any expected test data that might get generated. – Chris Nov 09 '17 at 15:03
  • @Chris: I've asked this question myself. Possible legitimate answers: 1) provider doesn't know about entries from script. OK. Then, EclipseLink should consult the "generator" table (as Hibernate does), but this doesn't happen. 2) Provider does know about the entries from script which it itself persists, in which case the available primary keys should be managed - but are not. IMHO, the sql pre-loading script should run in a separate transaction to make any sense for the purpose of testing. – badbishop Nov 10 '17 at 13:51
  • My question was more aimed at your expectations, not the possible solutions. Querying for the max ID value within a table for sequencing is an option, but usually very inefficient. The script you are executing is NOT a JPA script. It is just SQL statements that your provider passes through to the database, much as the setup script is. It does not processing or validation of the scripts, and does not understand anything in there. But the point of JPA is to manage java objects. Either update your populate scripts to increment sequence values, or use entities and JPA does it for you. – Chris Nov 10 '17 at 15:44

0 Answers0