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?

Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
  • 1
    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. – RJo Dec 05 '12 at 06:27
  • which JPA implementation? DataNucleus JPA allows you to specify where it is stored. Maybe other do too. Not part of the JPA spec obviously – DataNucleus Dec 05 '12 at 08:17

7 Answers7

13

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);
James
  • 17,965
  • 11
  • 91
  • 146
  • 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`? – Pavel_K Oct 09 '17 at 19:43
  • I have tried different variants and couldn't make it work. From here http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/p_persistencexml.htm `Currently, this property is used only for the canonical model generator. In the future, it may be used for customizing weaving and application bootstrapping.` – Pavel_K Oct 10 '17 at 15:19
8

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");
Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
  • 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. – niken Jul 05 '13 at 14:28
  • In Spring I would consider http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/new-in-3.1.html#new-in-3.1-jpa-without-xml in the first place – Evgeniy Dorofeev Jul 06 '13 at 03:39
  • 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? – njames Aug 12 '13 at 14:57
  • Thank you very much for nice idea. Based on you answer I found and posted answer for EclipseLink 2.7.0. – Pavel_K Oct 10 '17 at 18:01
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

Yogesh A Sakurikar
  • 1,731
  • 1
  • 11
  • 13
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.

poshjosh
  • 281
  • 2
  • 4
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.
Pavel_K
  • 10,748
  • 13
  • 73
  • 186
  • 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. – Tihamer Oct 04 '21 at 15:06
  • @Tihamer I am really glad that the post helped you. – Pavel_K Oct 04 '21 at 19:00
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

Khalil
  • 61
  • 3
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);
        
    }

}
krishna T
  • 425
  • 4
  • 14