Is it possible to have persistence.xml in a location other than META-INF?
Asked Answered
Y

7

13

I want to have my persistence.xml in conf folder of my app. How can I tell Persistence.createEntityManagerFactory that it should read it from there?

Yardage answered 5/12, 2012 at 6:8 Comment(3)
If you're using Spring, you can achieve this at least by using org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean and specifying the property persistenceXmlLocation to point to the file.Tremml
which JPA implementation? DataNucleus JPA allows you to specify where it is stored. Maybe other do too. Not part of the JPA spec obviouslyTrommel
So, After struggling with intellij/jboss/wildfly/maven the whole afternoon. I decided to use a node.js script Then, on Intellij wildfly configuration I added a "Before Launch "Script You can check my repo if you're interested in this apporach. github.com/talesmgodois/togglejta/blob/main/README.mdBegay
S
2

The createEntityManagerFactory methods search for persistence.xml files within the META-INF directory of any CLASSPATH element. if your CLASSPATH contains the conf directory, you could place an EntityManagerFactory definition in conf/META-INF/persistence.xml

Seychelles answered 5/12, 2012 at 6:34 Comment(1)
It seems I've found a working solution, please take a look at my answerYardage
B
12

If you are using EclipseLink you can set the persistence.xml location with the persistence unit property, "eclipselink.persistencexml".

properties.put("eclipselink.persistencexml", "/org/acme/acme-persistence.xml");
EntityManagerFactory factory = Persistence.createEntityManagerFactory("acme", properties);
Beeswax answered 5/12, 2012 at 13:59 Comment(2)
It it possible to use this solution when persistence.xml is outside jar, for example when persistence.xml path is /home/foo/downloads/persistence.xml?Otoplasty
I have tried different variants and couldn't make it work. From here eclipse.org/eclipselink/documentation/2.4/jpa/extensions/… Currently, this property is used only for the canonical model generator. In the future, it may be used for customizing weaving and application bootstrapping.Otoplasty
Y
7

This solution worked for me

    Thread.currentThread().setContextClassLoader(new ClassLoader() {
        @Override
        public Enumeration<URL> getResources(String name) throws IOException {
            if (name.equals("META-INF/persistence.xml")) {
                return Collections.enumeration(Arrays.asList(new File("conf/persistence.xml")
                        .toURI().toURL()));
            }
            return super.getResources(name);
        }
    });
    Persistence.createEntityManagerFactory("test");
Yardage answered 21/12, 2012 at 6:17 Comment(4)
Can you elaborate a bit more on your answer? I dont know enough about spring internals to interpolate where I need to add the above snippet.Effort
In Spring I would consider static.springsource.org/spring/docs/3.2.x/… in the first placeYardage
I would use your solution but it is not working with my configuration: Java 1.6.0_24, EclipseLink 2.3.2. I have an exception "javax.persistence.PersistenceException: No Persistence provider for EntityManager named XXX". Do you have an idea of why?Lengthy
Thank you very much for nice idea. Based on you answer I found and posted answer for EclipseLink 2.7.0.Otoplasty
S
2

The createEntityManagerFactory methods search for persistence.xml files within the META-INF directory of any CLASSPATH element. if your CLASSPATH contains the conf directory, you could place an EntityManagerFactory definition in conf/META-INF/persistence.xml

Seychelles answered 5/12, 2012 at 6:34 Comment(1)
It seems I've found a working solution, please take a look at my answerYardage
F
1

The ClassLoader may be a URLClassLoader, so try it this way:

        final URL alternativePersistenceXmlUrl = new File("conf/persistence.xml").toURI().toURL();

    ClassLoader output;

    ClassLoader current = Thread.currentThread().getContextClassLoader();

    try{

        URLClassLoader parent = (URLClassLoader)current;

        output = new URLClassLoader(parent.getURLs(), parent){

            @Override
            public Enumeration<URL> getResources(String name) throws IOException {

                if (name.equals("META-INF/persistence.xml")) {

                    return Collections.enumeration(Arrays.asList(alternativePersistenceXmlUrl));
                }

                return super.getResources(name);
            }
        };
    }catch(ClassCastException ignored) {

        output = new ClassLoader() {

            @Override
            public Enumeration<URL> getResources(String name) throws IOException {

                if (name.equals("META-INF/persistence.xml")) {

                    return Collections.enumeration(Arrays.asList(alternativePersistenceXmlUrl));
                }

                return super.getResources(name);
            }
        };
    }

It should work. Works for me under certain test etc conditions. Please this is a hack and should not be used in production.

Forefather answered 27/8, 2016 at 14:45 Comment(0)
O
1

My solution is for EclipseLink 2.7.0 and Java 9 and it is modified and detailed version of @Evgeniy Dorofeev answer.

In org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor on line 236 we see the following code:

URL puRootUrl = computePURootURL(descUrl, descriptorPath);

This code is used by EclipseLink to compute root url of the persistence.xml path. That's very important because final path will be made by adding descriptorPath to puRootUrl.

So, let's suppose we have file on /home/Smith/program/some-folder/persistence.xml, then we have:

Thread currentThread = Thread.currentThread();
ClassLoader previousClassLoader = currentThread.getContextClassLoader();
Thread.currentThread().setContextClassLoader(new ClassLoader(previousClassLoader) {
    @Override
    public Enumeration<URL> getResources(String name) throws IOException {
        if (name.equals("some-folder/persistence.xml")) {
            URL url = new File("/home/Smith/program/some-folder/persistence.xml").toURI().toURL();
            return Collections.enumeration(Arrays.asList(url));
        }
        return super.getResources(name);
    }

});
Map<String, String> properties = new HashMap<>();
properties.put("eclipselink.persistencexml", "some-folder/persistence.xml");
try {
    entityManagerFactory = Persistence.createEntityManagerFactory("unit-name", properties);
} catch (Exception ex) {
    logger.error("Error occured creating EMF", ex);
} finally {
    currentThread.setContextClassLoader(previousClassLoader);
}

Details:

  1. Pay attention that when creating new class loader I pass there previous classloader otherwise it doesn't work.
  2. We set property eclipselink.persistencexml. If we don't do that then default descriptorPath will be equal to META-INF/persistence.xml and we would need to keep our persistence.xml on /home/Smith/program/META-INF/persistence.xml to be found.
Otoplasty answered 10/10, 2017 at 17:59 Comment(2)
I searched and tried different methods for two days, trying to get my persistence.xml out of my distribution jar (so the end user could modify persistence.xml without needing to recompile). Since I am using EcliplseLink 2.5.2, this worked great for me.Laughingstock
@Laughingstock I am really glad that the post helped you.Otoplasty
O
0

I tried these ways when the program is starting (at first line of main function):

  • Write your persistence.xml to the resources/META-INF/persistence.xml of the jar

    I had problem with this way: Java write .txt file in resource folder

  • Create META-INF folder in the jar directory and put your persistence.xml into it, then execute this command:

    jar uf $jarName META-INF/persistence.xml

    This command will replace META-INF/persistence.xml (your file) in the jar

    private fun persistence() {
        val fileName = "META-INF/persistence.xml"
        val jarName: String?
        val done = try {
            jarName = javaClass.protectionDomain.codeSource.location.path
            if (File(fileName).exists() && !jarName.isNullOrBlank()
                    && jarName.endsWith(".jar") && File(jarName).exists()) {
                Command().exec("jar uf $jarName META-INF/persistence.xml", timeoutSec = 30)
                true
            } else false
        } catch (e: Exception) {
            false
        }
        if (done) {
            logger.info { "$fileName exist and will be loaded :)" }
        } else {
            logger.info {
                "$fileName not exist in current folder so it will be read from .jar :(" +
                        " you can run: jar uf jarName.jar META-INF/persistence.xml"
            }
        }
    }

Running Command Line in Java

Orle answered 4/1, 2021 at 13:52 Comment(0)
E
0

A solution by creating tweaked PersistenceUnitDescriptor.

import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
import org.hibernate.jpa.boot.spi.Bootstrap;
import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder;


public class HibernateEntityManagerFactoryBuilder {

    public static final EntityManagerFactory build(URL xmlUrl) {
        final ParsedPersistenceXmlDescriptor xmlDescriptor = PersistenceXmlParser.locateIndividualPersistenceUnit(xmlUrl);
        final HibernatePersistenceUnitDescriptor hibernateDescriptor = new HibernatePersistenceUnitDescriptor(xmlDescriptor);
        final EntityManagerFactoryBuilder builder = Bootstrap.getEntityManagerFactoryBuilder(hibernateDescriptor, Collections.emptyMap(), (ClassLoader) null);
        final EntityManagerFactory factory = builder.build();
        return factory;
    }
    
    
    public static final EntityManagerFactory build(URL xmlUrl, final String name) {
        final ParsedPersistenceXmlDescriptor xmlDescriptor = PersistenceXmlParser.locateNamedPersistenceUnit(xmlUrl, name);
        if(xmlDescriptor == null) throw new RuntimeException("Persistence unit with name '"+name+ "' not found.");
        final HibernatePersistenceUnitDescriptor hibernateDescriptor = new HibernatePersistenceUnitDescriptor(xmlDescriptor);
        final EntityManagerFactoryBuilder builder = Bootstrap.getEntityManagerFactoryBuilder(hibernateDescriptor, Collections.emptyMap(), (ClassLoader) null);
        final EntityManagerFactory factory = builder.build();
        return factory;
    }
    
    public static void main(String[] args) {
        try {
            final EntityManagerFactory factory = build(new File("D:/ini/persistence.xml").toURI().toURL());
        } catch (Exception e) {e.printStackTrace();}
    }
}


public class HibernatePersistenceUnitDescriptor implements PersistenceUnitDescriptor {

    private final PersistenceUnitDescriptor descriptor;
    
    public HibernatePersistenceUnitDescriptor(PersistenceUnitDescriptor descriptor) {
        this.descriptor = descriptor;
    }

    @Override
    public URL getPersistenceUnitRootUrl() {
        return null;
    }

    @Override
    public String getName() {
        return descriptor.getName();
    }

    @Override
    public String getProviderClassName() {
        return descriptor.getProviderClassName();
    }

    @Override
    public boolean isUseQuotedIdentifiers() {
        return descriptor.isUseQuotedIdentifiers();
    }

    @Override
    public boolean isExcludeUnlistedClasses() {
        return descriptor.isExcludeUnlistedClasses();
    }

    @Override
    public PersistenceUnitTransactionType getTransactionType() {
        return descriptor.getTransactionType();
    }

    @Override
    public ValidationMode getValidationMode() {
        return descriptor.getValidationMode();
    }

    @Override
    public SharedCacheMode getSharedCacheMode() {
        return descriptor.getSharedCacheMode();
    }

    @Override
    public List<String> getManagedClassNames() {
        return descriptor.getManagedClassNames();
    }

    @Override
    public List<String> getMappingFileNames() {
        return descriptor.getMappingFileNames();
    }

    @Override
    public List<URL> getJarFileUrls() {
        return descriptor.getJarFileUrls();
    }

    @Override
    public Object getNonJtaDataSource() {
        return descriptor.getNonJtaDataSource();
    }

    @Override
    public Object getJtaDataSource() {
        return descriptor.getJtaDataSource();
    }

    @Override
    public Properties getProperties() {
        return descriptor.getProperties();
    }

    @Override
    public ClassLoader getClassLoader() {
        return descriptor.getClassLoader();
    }

    @Override
    public ClassLoader getTempClassLoader() {
        return descriptor.getTempClassLoader();
    }

    @Override
    public void pushClassTransformer(EnhancementContext enhancementContext) {
        descriptor.pushClassTransformer(enhancementContext);
        
    }

}
Eriha answered 30/11, 2021 at 11:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.