14

Problem

I'm writing a standalone utility program which, given a jar containing a JPA-2 annotated persistence unit, needs to programmatically get a list of all my @Entity classes in a particular persistence unit.

I'd like to decide which of 2 approaches would be the way to go to get this information, and why; or if there is another better way I haven't thought of.

Solution 1

Java program puts jar on the classpath, creates persistence unit from the classes in the jar using JavaSE methodologies. Then it uses the javax.persistence classes to get the JPA Metamodel, pull back list of class tokens from that.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("MY_ PERSISTENCE_UNIT");
Metamodel mm = emf.getMetamodel();

// loop these, using getJavaType() from Type sub-interface to get 
// Class tokens for managed classes.
mm.getManagedTypes();

Solution 2

Program scan the directories and files inside the specified jar for persistence.xml files, then finds one with the specified persistence unit name. Then XPath the file to get the list of <class> XML elements and read the fully qualified class names from there. From names, build class tokens.

Constraints/Concerns

  • I'd like to go with approach 1 if possible.
  • This utility will NOT run inside a container, but the jar is an EJB project designed to run inside one. How will this be a problem?
  • The utility will have Open-EJB available on the classpath to get implementations of all the Java EE 6 classes.
  • Even though the EJB project is built to run on Hibernate, the utility should not be Hibernate-specific.
  • Are there any stumbling blocks?
Tom Tresansky
  • 19,364
  • 17
  • 93
  • 129

2 Answers2

14

In case anyone's interested, Solution 1 worked. Here's essentially what I had to do:

public MySQLSchemaGenerator() throws ClassNotFoundException {
    Properties mySQLDialectProps = new Properties();
    mySQLDialectProps.setProperty("javax.persistence.transactionType", "RESOURCE_LOCAL");
    mySQLDialectProps.setProperty("javax.persistence.jtaDataSource", "");

    final EntityManagerFactory emf = Persistence.createEntityManagerFactory("<persistence_unit_name>", mySQLDialectProps);
    final Metamodel mm = emf.getMetamodel();
    for (final ManagedType<?> managedType : mm.getManagedTypes()) {
      managedType.getJavaType(); // this returns the java class of the @Entity object
    }
  }

The key was to override my transaction type and blank out the jtaDataSource which had been defined in my persistence.xml. Turns out everything else was unnecessary.

Tom Tresansky
  • 19,364
  • 17
  • 93
  • 129
1

If Your jar is well-formed (persistence.xml at the right place - in the META-INF folder), then all looks fine.

It is not necessary to run your utility inside a container, JPA is not a part of JavaEE specs.

  • My jar is well formed, the beans work fine when I deploy on JBoss. I'm actually progressing pretty well going down route 1. I had to figure out to to override a bunch of settings like Transaction-Type (was set to JTA, needed to be Resource_Local) in the persistence unit (if I comment them out the util runs fine, but then I can't run okay deployed to the app server). There are a couple of issues remaining with overriding the datasource...hopefully will get a chance to work a little more on it soon and will post the code I've got once it totally working. – Tom Tresansky Apr 13 '12 at 16:05
  • Of course JPA is part of JEE spec it's the JSR. You can run it outside a JEE container just because there are some standalone implementation like hibernate. (hibernate is not just a JPA implementation and it existed before the specification, they just provided compliance) – Gab Jul 29 '13 at 18:59