1

Does JPA support optional persistence units and if so how do I configure that?

I do have one persistence unit which is my main database.

Then I configured another one where I just read objects from the db to do some checks. To avoid messing around with transactions on multiple datasources I set the second datasource jta="false".

But I like to be able to check if a second datasource was defined at all and only use it if it is there. If it is not defined I'd like to simply skip my checks.

The problem is that I can not find how to make this optional. If the second datasource is not configured I get a New missing/unsatisfied dependencies error from the deployment scanner in Jboss when my war is deployed:

service jboss.naming.context.java.secondDatasource (missing) dependents: [service jboss.persistenceunit."de.my.war#secondDatasource"] 

(Btw: I am using JBoss 7.1.0 and configured the datasources in standard.xml - if that info is of any relevance.)

Any hints?

2 Answers 2

4
+25

I'm not sure it's possible with XML configuration which is basically a convention of how JPA should be used in most of the cases. Try to look for a programmatic approach.

Perhaps you could programmatically lookup the datasource as JNDI resource, and if there is one found, you could build an EntityManager yourself. With the help of CDI it might even not be that difficult as you would think.

@Produces
@MyAlterNativeEntityManager
public EntityManager getEntityManager {
    EntityManager entityManager = null;
    if(jndiLookupMySecondDatasource()){
       entityManager = buildEntityManagerProgrammatically(...);
    }
    return entityManager;
}

Of course if the datasource is not found, an empty EntityManager will be returned but then in the calling code you could simply check if it was initialized and use only if it was. Hope that helps to put you on the right way.

8
  • Thanks for the idea. I think this already happens when my persistence.xml is processed. But I will check this...
    – Jens
    Commented Mar 6, 2013 at 14:46
  • Of course, but this way you don't even have to declare the second datasource in the persistence.xml! Commented Mar 6, 2013 at 21:57
  • @Balzás Mária Németh: Sorry for my delayed answer to this. I tried your solution of building an EntityManager myself if there is a datasource found via JNDI. But: I don't know how to set the found datasource on my self-created EntityManager resp. EntityManagerFactory. It only accepts properties to create one. But I have a datasouce at hand. I thought about mapping all necessary properties myself but the password is something I can not find on the datasource. Is there a way to let the created EntityManager use my datasource?
    – Jens
    Commented Mar 18, 2013 at 10:10
  • I don't think so. As the other answer of Glen Best also mentions, EMF must relate to one PU, so you cannot replace its datasource, and there's no point creating one without a datasource. If there's a datasource, create the EMF, if there isn't, you cannot create one. Is this answer useful to you? Or I might have misunderstood the question. Commented Mar 18, 2013 at 10:32
  • I thought I could create a second EntityManagerFactory for the second datasource. So this is a second PersistenceUnit. All of this already works if I define it in the persistence.xml file. The only problem is that I am running into exceptions when the second datasource is not defined (in my standalone.xml file on Jboss). Following your idea I thought I could shift the creation of the second EntityManagerFactory to runtime so I can avoid the exceptions on startup.
    – Jens
    Commented Mar 18, 2013 at 10:56
1

In the JPA standard:

  • Each EntityManagerFactory (EMF) must relate to one PersistentUnit (PU) - not possible to have multiple PersistenceUnits nor zero PersistenceUnits.
  • Each EntityManager (EM) instance is created from an EMF and so also has a single PU. Of course, can create multiple EMs from an EMF, all using the same PU.
  • Can configure which PU to use explicitly against each EMF using hard coding

    EntityManagerFactory emf = Persistence.createEntityManagerFactory("EmployeeService");

    or by injecting the EMF using annotations

    @PersistenceUnit("EmployeeService") EntityManagerFactory emf;

    or can leave it blank and just pick up the default PU as configured in persistence.xml.

  • Can configure which PU to use explicitly against each EM by injecting the EM using annotations. Here the code doesn't refer to the EMF at all (letting the container reference an EMF invisibly)

    @PersistenceContext(unitName="EmployeeService") EntityManager em;

In all of this the idea of optional PUs doesn't make any sense. If you are using JPA EMs, you must commit to a PU.

Options:

  1. Edit persistence.xml to have the same logical persistence unit "switch" between two different physical database instances. Doesn't meet full requirements - will still read values from alternative data source.
  2. Lookup/inject an environment variable ("debug" or not) and programmatically flick between different data sources as per solution of Balazs Maria Nemeth. Doesn't meet full requirements, because it will still read values from alternative data source.
  3. Lookup/inject an environment variable ("debug" or not) and run/exclude code for your alternative EM appropriately.

3 is only option that meets full requirements. E.g.:

// Inject environment setting. Resource annotation works with/without CDI -  
// just doesn't give scoping support, which isn't required here.
@Resource boolean debugMode;

if (debugMode) {
    @PersistenceContext(unitName="DebugPersistenceUnit")
    EntityManager debugEM;

    Employee emp = debugEM.find(Employee.class, empId);
}

Then in your web.xml or ejb.xml include an env-entry:

<env-entry>
    <description>
     Only set this true in a debugging environment
    </description>
    <env-entry-name>debugMode</env-entry-name>
    <env-entry-type>java.lang.Boolean</env-entry-type>
    <env-entry-value>true</env-entry-value>
</env-entry>

Not the answer you're looking for? Browse other questions tagged or ask your own question.