domingo, 28 de noviembre de 2021

La API de JPA 2.1 (VIII) JPA sin persistence.xml

1. Introducción 

Se pueden crear persistence units programaticamente en estas condiciones:


  1. Utilizando Spring (ver stack overflow)
  2. Utilizando Hibernate (ver el mismo link en  stack overflow)
  3. Implementando la interface PersistenceProvider (ver el mismo link en stack overflow y JPA API

Cogiendo un poco de cada uno he creado las siguientes clases
  1. MyConnectionProps (propiedades de la conexión)
  2. PersistenceUnitInfoEdu (clase intermedia que sustituye al persistence.xml)
  3. JPANoPersistencexmlUtils
Recordar que para que funcione hay que crear la BD en H2!

Veamos la primera

2. MyConnectionProps

Como se puede ver, hemos definido propiedades para 2 conexiones, la primera a una BD Postgres, y la segunda para una BD H2.

package openadmin.utils.nopersistencexml;
import java.util.Properties; public class MyConnectionProps { /** * Connection for a Postgres DB * @return */ public static Properties getPropsPostgres() { Properties props=new Properties(); props.put("hibernate.dialect" , "org.hibernate.dialect.PostgreSQLDialect"); props.put("hibernate.hbm2ddl.auto" , "update"); props.put("hibernate.show_sql" , "true"); props.put("hibernate.cache.provider_class" , "org.hibernate.cache.HashtableCacheProvider"); props.put("hibernate.connection.url" , "jdbc:postgresql://192.168.11.11:5432/myDB"); props.put("hibernate.connection.driver_class" , "org.postgresql.Driver"); props.put("hibernate.connection.username" , "my_user"); props.put("hibernate.connection.password" , "my_password"); return props; } /** * /** * Connection for a H2 DB * @return */ public static Properties getPropsH2() { Properties props=new Properties(); props.put("hibernate.dialect" , "org.hibernate.dialect.H2Dialect"); props.put("hibernate.hbm2ddl.auto" , "update");
		props.put("hibernate.show_sql"                , "true");
		props.put("hibernate.cache.provider_class"    , "org.hibernate.cache.HashtableCacheProvider");
		props.put("hibernate.connection.url"          , "jdbc:h2:tcp://localhost/~/test");		
		props.put("hibernate.connection.driver_class" , "org.h2.Driver");
		props.put("hibernate.connection.username"     , "my_user");
		props.put("hibernate.connection.password"     , "my_password");
		return props;
	}
}

2. PersistenceUnitInfoEdu 


Esta clase se quedará ahí y solo la referenciaremos. Es una clase que guarda las propiedades que se tendrían en el persistence.xml

package openadmin.utils.nopersistencexml;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.persistence.spi.ClassTransformer;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.sql.DataSource;

import lombok.Getter;
import lombok.Setter;

public class PersistenceUnitInfoEdu implements PersistenceUnitInfo {
    
    public static String JPA_VERSION = "2.2";
    @Getter @Setter private String persistenceUnitName; //OK
    @Getter @Setter private PersistenceUnitTransactionType transactionType   = PersistenceUnitTransactionType.RESOURCE_LOCAL; //OK
    @Getter @Setter private String persistenceProviderClassName="org.hibernate.jpa.HibernatePersistenceProvider"; //OK
    @Getter @Setter private DataSource jtaDataSource=null; //OK a falta de JNI
    @Getter @Setter private DataSource nonJtaDataSource=null; //OK a falta del no JNI
    @Getter @Setter private List<String> mappingFileNames = new ArrayList<>(); //OK
    @Getter @Setter private List<URL> jarFileUrls=null; //OK
    @Getter @Setter private URL persistenceUnitRootUrl=null; //OK
    @Getter @Setter private List<String> managedClassNames = new ArrayList<>(); //OK
    @Getter @Setter private boolean excludeUnlistedClasses=false; //OK
    @Getter @Setter private SharedCacheMode sharedCacheMode=null; //OK
    @Getter @Setter private ValidationMode validationMode=ValidationMode.AUTO; //OK
    @Getter @Setter private Properties properties=null; //OK
    @Getter @Setter private String persistenceXMLSchemaVersion=JPA_VERSION; //OK
    @Getter @Setter private ClassLoader classLoader=null; //OK
    @Getter @Setter private List<ClassTransformer> transformers = new ArrayList<>(); //OK addTransformes
    @Getter @Setter private ClassTransformer transformer=null;
    @Getter @Setter private ClassLoader newTempClassLoader=null; //OK
    
    public PersistenceUnitInfoEdu(String persistenceUnitName, List<String> managedClassNames, boolean excludeUnlistedClasses, Properties properties) {
        this.persistenceUnitName = persistenceUnitName;
        this.managedClassNames = managedClassNames;
        this.excludeUnlistedClasses=excludeUnlistedClasses;
        this.properties = properties;
    }

	@Override
	public boolean excludeUnlistedClasses() {
		return excludeUnlistedClasses;
	}

	@Override
	public void addTransformer(ClassTransformer transformer) {
		// TODO Auto-generated method stub
	}

	
	
}


3. JPANoPersistencexmlUtils


Clase con métodos estáticos para poder crear finalmente el EntityManagerFactory, y que en el "main" hemos hecho una prueba para abrir una BD H2 y leer los registros de la tabla "TEST". Si no estuviera creada la tabla, como hemos incluido la opción de actualizar la BD, crearía esta tabla.

package openadmin.utils.nopersistencexml;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;
import javax.persistence.spi.PersistenceUnitInfo;

import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl;
import org.hibernate.jpa.boot.internal.PersistenceUnitInfoDescriptor;

import openadmin.utils.HibernateUtils2;
import openadmin.utils.Test;

public class JPANoPersistenceUtils {
	
	private static PersistenceUnitInfoEdu getPsInitInfo(String persistenceUnitName, List<String> managedClassNames, boolean excludeUnlistedClasses, Properties properties) {
		return new PersistenceUnitInfoEdu(persistenceUnitName, managedClassNames, excludeUnlistedClasses, properties);
	}
	
	public static EntityManagerFactory getEntityManagerFactory(String persistenceUnitName, List<String> managedClassNames, boolean excludeUnlistedClasses, Properties properties) {
		PersistenceUnitInfo persistenceUnitInfo = getPsInitInfo(persistenceUnitName,managedClassNames,excludeUnlistedClasses,properties);
		Map<String, Object> configuration = new HashMap<>();
		 return new EntityManagerFactoryBuilderImpl(
		          new PersistenceUnitInfoDescriptor(persistenceUnitInfo), configuration)
		          .build();
	}

	public static void main(String[] args) {
		List<String>classes=new ArrayList<String>();
		classes.add("openadmin.utils.nopersistencexml.Test");
		EntityManagerFactory emf=getEntityManagerFactory("my-h2",classes,true,MyConnectionPrtops.getPropsH2());
		
		
		EntityManager em=emf.createEntityManager();
		
		//Query q=em.createNativeQuery("SELECT * FROM TEST");
		Query q=em.createQuery("SELECT t FROM Test t WHERE t.id<1000");
		List<Test> ltest=q.getResultList();
		for (Test t:ltest) System.out.println(t.getId()+"-"+ t.getName());
			
		em.close();
		emf.close();
	}

}

Las líneas de amarillo són las que aportan la información al respecto

Veamos ahora elementos complementarios:

4. La entidad Test

Esta clase es la que se ha hecho la persistencia
package openadmin.utils.nopersistencexml;
import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.Getter; import lombok.Setter; @Entity @Table(name = "TEST") public class Test { @Id @Column(name="ID") @Getter @Setter private Integer id; @Getter @Setter @Column(name="NAME") private String name; //Getters and setters omitted for brevity }

Veamos ahora el build.gradle

5. build.gradle

Tenga cuidado de incluir javax.validation solo para versiones de Hibernate >=5.5, en las versiones anteriores debe eliminar esta entrada.


/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Java library project to get you started.
 * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
 * User Manual available at https://docs.gradle.org/7.0.2/userguide/building_java_projects.html
 */

plugins {
    // Apply the java-library plugin for API and implementation separation.
    id 'java-library'    
}

repositories {
    // Use JCenter for resolving dependencies.
    jcenter()
    mavenCentral()
}


// My customization 
//project.jar.destinationDirectory = file("$rootDir/mytarget")
project.jar.destinationDirectory = file("$rootDir/../mytargets")  
project.archivesBaseName = 'c-dao' 
project.version = '1.0'

dependencies {
    // Use JUnit test framework.
    testImplementation 'junit:junit:4.13'

	/***********************************************************************
    *********************** LOMBOK DEFINITION ******************************/
    compileOnly             'org.projectlombok:lombok:1.18.20'
    annotationProcessor     'org.projectlombok:lombok:1.18.20'
    testCompileOnly         'org.projectlombok:lombok:1.18.20'
    testAnnotationProcessor 'org.projectlombok:lombok:1.18.20'
    /************************************************************************/
    
   /* Apache.commons  */
    implementation "org.apache.commons:commons-exec:1.3"
    
    /* Apache general */
    implementation "commons-io:commons-io:2.11.0"
    
    implementation "org.apache.commons:commons-lang3:3.12.0"
    
    /* Jakarta --> Need plugin "war" */
    //providedCompile "jakarta.annotation:jakarta.annotation-api:2.0.0"
    compileOnly "jakarta.annotation:jakarta.annotation-api:2.0.0"
    
    /* javax.validation--> Use only for hibernate >=5.5 Versions.*/
    /* In lower hibernate versions , remove this entry!!!!!!  */
    implementation "javax.validation:validation-api:2.0.1.Final"
    
    
    /* HIbernate */
    implementation "org.hibernate:hibernate-core:5.4.31.Final"
    implementation "org.hibernate:hibernate-envers:5.4.31.Final"
    //implementation "org.hibernate:hibernate-testing:5.4.31.Final"
    implementation "org.hibernate:hibernate-validator:7.0.1.Final" 
    // https://mvnrepository.com/artifact/org.hibernate/hibernate-entitymanager
    implementation 'org.hibernate:hibernate-entitymanager:5.4.31.Final'
    
    /*DB Drivers */
    implementation "org.postgresql:postgresql:42.2.20"
    implementation "net.sourceforge.jtds:jtds:1.3.1"
   
    implementation 'com.h2database:h2:1.4.200'
   
}

jar {   
    from sourceSets.main.allSource  //Include java sources
}