Evgeniy Dorofeev
Evgeniy Dorofeev

Reputation: 136122

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

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?

Upvotes: 13

Views: 22052

Answers (7)

krishna T
krishna T

Reputation: 418

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);
        
    }

}

Upvotes: 0

Khalil
Khalil

Reputation: 61

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

Upvotes: 0

user5182503
user5182503

Reputation:

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.

Upvotes: 1

poshjosh
poshjosh

Reputation: 331

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.

Upvotes: 1

Evgeniy Dorofeev
Evgeniy Dorofeev

Reputation: 136122

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");

Upvotes: 8

James
James

Reputation: 18389

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);

Upvotes: 12

Yogesh A Sakurikar
Yogesh A Sakurikar

Reputation: 1881

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

Upvotes: 2

Related Questions