62mkv
62mkv

Reputation: 1542

How to have JPA with Hibernate provider auto-scan for entity classes

there's a LOT of questions on this on the internets.. however I still struggle with what seems to be a quite typical scenario.

I'm doing a quite simple JPA (using Hibernate) project, with Entity classes being resided with a com.actions.domain package

I have persistence.xml as follows:

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
             http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
             version="2.1">

    <persistence-unit name="my-persistence-unit">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

        <properties>
            <property name="javax.persistence.jdbc.driver"
                      value="org.h2.Driver" />

            <property name="javax.persistence.jdbc.url"
                      value="jdbc:h2:~/test.h2db" />

            <property name="hibernate.archive.autodetection" value="class, hbm"/>
        </properties>

    </persistence-unit>

</persistence>

this file resides in src\test\resources\META-INF folder

I have also JUnit test classes written like this:

protected EntityManager entityManager;

@Before
public void setUp() throws Exception {
    EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("my-persistence-unit");
    entityManager = entityManagerFactory.createEntityManager();
}

@Test
public void actionsAdded_success() {
    entityManager.getTransaction().begin();
    entityManager.persist(new Action("TEST_ACTION1", "ACTION"));
    entityManager.persist(new Action("TEST_ACTION2", "ACTION"));
    entityManager.getTransaction().commit();
}

An example of an Entity:

@Entity
@Table(name = "UC_ACTIONS")
@Data
public class Action {

    @Id
    @GeneratedValue(generator="increment")
    private Long id;

    private String name;

    private String type;

    public Action(String name, String type) {
        this.name = name;
        this.type = type;
    }
}

If I put a <class>com.actions.domain.Action</class> element into persistence unit description, it all works like a charm, however I don't want to enumerate the classes here by hand, also don't want to have any hibernate.cfg.xml file

So if I remove that element, and try to somehow have provider auto-scan for entities, I get errors like Unknown entity: com.actions.domain.Action

I've tried running these tests both a Run configuration in IntelliJ and as a gradle test command. In none of the cases META-INF folder is in the same directory as classes, so how are people achieving this ??

How on Earth can one have it working ?

UPDATE: there's an accepted answer to identical question here: https://stackoverflow.com/a/16598682/2583044 Still, I'm struggling to believe that such a main-stream use case issue could have such a marginal dirty hack of a solution :(

Upvotes: 4

Views: 2973

Answers (3)

Dovias
Dovias

Reputation: 1

Based on answer provided by Seymur Asadov, this can be done in Gradle Kotlin DSL using processResources task:

tasks {
    processResources {
        val currentSourceSet = sourceSets.find { sourceSet ->
            sourceSet.resources.any {
                source.contains(it)
            }
        }

        val properties = "classesDirectory" to currentSourceSet!!.output.classesDirs.singleFile.relativeTo(projectDir)
        filesMatching("META-INF/persistence.xml") {
            expand(properties)
        }
    }
}

and by adding expanding property in META-INF/persistence.xml <jar-file>...</jar-file> XML tags:

<persistence-unit name="application">
    <jar-file>${classesDirectory}</jar-file>
    <!-- your persistence unit data -->
</persistence-unit>

This way, Gradle will expand current source set compiled classes directory property for the META-INF/persistence.xml file to use.

Upvotes: 0

62mkv
62mkv

Reputation: 1542

The issue was resolved (thanks to Vlad Mihalcea) ultimately as follows:

  1. Moved the META-INF folder under src/main
  2. Added the following to build.gradle:

    apply plugin: 'idea'

    idea { module { inheritOutputDirs = true } }

    sourceSets { main { output.resourcesDir = "build/classes" } test { output.resourcesDir = "build/classes" } }

Afterwards, tests are working as expected both from IDEA and with Gradle.

Upvotes: -1

Seymur Asadov
Seymur Asadov

Reputation: 672

By default autodetection works for entities in the same classpath item as persistence.xml. It can be configured by elements.

To enable correct autodetection when persistence.xml is in src/test/resources/META-INF I use the following trick:

persistence.xml

<persistence ...>
      <persistence-unit ...>
            <jar-file>${project.build.outputDirectory}</jar-file>
            ...
      </persistence-unit>
  </persistence>

pom.xml - enable resource filtering for src/test/resources:

<project ...>
    ...
    <build>
        <testResources>
            <testResource>
                <directory>src/test/resources</directory>
                <filtering>true</filtering>
            </testResource>
        </testResources>
    </build>
</project>

Upvotes: 3

Related Questions