At Hibernate 4.3 Ejb3Configuration class was removed. This class was commonly used for creating hibernate configuration file from a persistence unit (persistence.xml file) to SchemaExport tool.
As a simple alternative to export schema to .sql file I'm using the following code:
public static void export(String persistenceUnit, String exportFileName) {
Map<String, String> hash = new HashMap<String, String>();
hash.put("hibernate.hbm2ddl.auto", "create-drop");
EntityManagerFactory factory = Persistence.createEntityManagerFactory(
persistenceUnit, hash);
org.hibernate.jpa.internal.EntityManagerFactoryImpl hibFactory = (org.hibernate.jpa.internal.EntityManagerFactoryImpl) factory;
SessionFactoryImpl hibSessionFactory = hibFactory.getSessionFactory();
SchemaExport schema = ReflectUtils.getPrivateFieldValue(
hibSessionFactory, "schemaExport");
schema.setOutputFile(exportFileName);
schema.setFormat(false);
schema.setDelimiter(";");
schema.drop(true, false);
schema.create(true, false);
}
At this piece of code, i'm basically using schemaexport object created by HibernateSessionFactoryImpl. The drawback is that every time it´s executed the database schema is recreated. Is there any other simple way to use SchemaExporTool with Hibernate 4.3 and JPA? It seems that the real question is how to create Hibernate Configuration Object from a persistenceunit?
I ran into the same Problem. I ended up by using the internal PersistenceXmlParser of Hibernate to access information in the persistence.xml file and creating the Configuration object manually:
public static void main(String[] args) {
PersistenceXmlParser parser = new PersistenceXmlParser(new ClassLoaderServiceImpl(), PersistenceUnitTransactionType.RESOURCE_LOCAL);
List<ParsedPersistenceXmlDescriptor> allDescriptors = parser.doResolve(new HashMap<>());
for (ParsedPersistenceXmlDescriptor descriptor : allDescriptors) {
Configuration cfg = new Configuration();
cfg.setProperty("hibernate.hbm2ddl.auto", "create");
cfg.setProperty("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
cfg.setProperty("hibernate.id.new_generator_mappings", "true");
List<String> managedClassNames = descriptor.getManagedClassNames();
for (String className : managedClassNames) {
try {
cfg.addAnnotatedClass(Class.forName(className));
} catch (ClassNotFoundException e) {
System.out.println("Class not found: " + className);
}
}
SchemaExport export = new SchemaExport(cfg);
export.setDelimiter(";");
export.setOutputFile("C:\\dev\\" + descriptor.getName() + "_create_schema.sql");
export.setFormat(true);
export.execute(true, false, false, false);
}
}
Related
I have a project with supporting XML-config. Now we wanna add hibernate config for entities via annotation. Seems it possible and works (correct me if I am mistaken). The issue is that our system
private String entityName() {
String name = null;
try {
String longName = getDaoFactory().getSessionFactory().getClassMetadata(entityClass).getEntityName();
name = longName.substring(longName.lastIndexOf(".") + 1);
} catch (Exception ex) {
log.error("Exception getting name from hibernate for class: " + entityClass);
name = null;
}
return name;
}
This method works well for entities with xml-config, but how to get a name for an entity which is configurated by annotation?
Thank you in advance for any recommendation and suggestion :)
UPD:
from init session factory method:
Configuration configuration = new Configuration();
configuration.addResource("resources/administrator/queries.hbm.xml");
StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder().applySettings(configuration
.getProperties());
SessionFactory sessionFactory = configuration.buildSessionFactory(builder.build());
setSessionFactory(sessionFactory);
return configuration;
I created a session Factory like this.
public static SessionFactory buildSessionFactoryFromAnnotatedClasses(){
StandardServiceRegistry standardRegistry =
new StandardServiceRegistryBuilder().applySettings(// pass org.hibernate.cfg.Configuration object here)
.getProperties()).build(); // Configuration object has properties
MetadataSources sources = new MetadataSources(standardRegistry);
sources.addAnnotatedClass(MyClass.class);// MyClass is your java annotated class
Metadata metaData = sources.getMetadataBuilder().build();
return metaData.getSessionFactoryBuilder().build();
}
I have multiple instances on db2. How can I configure hibernate config file so that I can use all of them?
You can specify it by schema element while defining table for your entity.
#Table(name="TABLE_NAME", schema="SCHEMA_NAME")
Else, you can use separate EntityManager pointing to respective schema & then use the same entity, as their structure is similar.
You can have separate configuration files for each schema & then build SessionFactory from it, below is some pseudo-code for it.
SessionFactory sf_1 = new Configuration().configure("schema1config.cfg.xml").buildSessionFactory();
SessionFactory sf_2 = new Configuration().configure("schema2config.cfg.xml").buildSessionFactory();
session_1 = sf_1.openSession(); //-- Similarly for other
You can refer this link for further details to map multiple schema, but it isn't hibernate specific.
Credit goes to #Nayan Wadekar
Resource Link:
How to connect to multiple databases in Hibernate
Found solution over link as per my requirement:
Setting properties programmatically in Hibernate
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from standard (hibernate.cfg.xml)
// config file.
Properties c = new Properties();
c.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
c.setProperty("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
c.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/mydiscou_billing?zeroDateTimeBehavior=convertToNull");
c.setProperty("hibernate.connection.username", "root");
c.setProperty("hibernate.connection.password", "123");
c.setProperty("hibernate.connection.autoReconnect", "true");
c.setProperty("connection.provider_class", "org.hibernate.connection.C3P0ConnectionProvider");
c.setProperty("c3p0.min_size", "5");
c.setProperty("c3p0.max_size", "20");
c.setProperty("c3p0.timeout", "1800");
c.setProperty("c3p0.max_statements", "100");
c.setProperty("hibernate.c3p0.testConnectionOnCheckout", "true");
sessionFactory = new AnnotationConfiguration().setProperties(c).configure().buildSessionFactory();
} catch (Throwable ex) {
// Log the exception.
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
With Spring 4 and Hibernate 4, I was able to use Reflection to get the Hibernate Configuration object from the current environment, using this code:
#Autowired LocalContainerEntityManagerFactoryBean lcemfb;
EntityManagerFactoryImpl emf = (EntityManagerFactoryImpl) lcemfb.getNativeEntityManagerFactory();
SessionFactoryImpl sf = emf.getSessionFactory();
SessionFactoryServiceRegistryImpl serviceRegistry = (SessionFactoryServiceRegistryImpl) sf.getServiceRegistry();
Configuration cfg = null;
try {
Field field = SessionFactoryServiceRegistryImpl.class.getDeclaredField("configuration");
field.setAccessible(true);
cfg = (Configuration) field.get(serviceRegistry);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
SchemaUpdate update = new SchemaUpdate(serviceRegistry, cfg);
With Hibernate 5, I must use some MetadataImplementor, which doesn't seems to be available from any of those objects. I also tried to use MetadataSources with the serviceRegistry. But it did say that it's the wrong kind of ServiceRegistry.
Is there any other way to get this working?
Basic idea for this problem is:
implementation of org.hibernate.integrator.spi.Integrator which stores required data to some holder. Register implementation as a service and use it where you need.
Work example you can find here https://github.com/valery-barysok/spring4-hibernate5-stackoverflow-34612019
create org.hibernate.integrator.api.integrator.Integrator class
import hello.HibernateInfoHolder;
import org.hibernate.boot.Metadata;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
public class Integrator implements org.hibernate.integrator.spi.Integrator {
#Override
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
HibernateInfoHolder.setMetadata(metadata);
HibernateInfoHolder.setSessionFactory(sessionFactory);
HibernateInfoHolder.setServiceRegistry(serviceRegistry);
}
#Override
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
}
}
create META-INF/services/org.hibernate.integrator.spi.Integrator file
org.hibernate.integrator.api.integrator.Integrator
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.hbm2ddl.SchemaUpdate;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... args) throws Exception {
new SchemaExport((MetadataImplementor) HibernateInfoHolder.getMetadata()).create(true, true);
new SchemaUpdate(HibernateInfoHolder.getServiceRegistry(), (MetadataImplementor) HibernateInfoHolder.getMetadata()).execute(true, true);
}
}
I would like to add up on Aviad's answer to make it complete as per OP's request.
The internals:
In order to get an instance of MetadataImplementor, the workaround is to register an instance of SessionFactoryBuilderFactory through Java's ServiceLoader facility. This registered service's getSessionFactoryBuilder method is then invoked by MetadataImplementor with an instance of itself, when hibernate is bootstrapped. The code references are below:
Service Loading
Invocation of getSessionFactoryBuilder
So, ultimately to get an instance of MetadataImplementor, you have to implement SessionFactoryBuilderFactory and register so ServiceLoader can recognize this service:
An implementation of SessionFactoryBuilderFactory:
public class MetadataProvider implements SessionFactoryBuilderFactory {
private static MetadataImplementor metadata;
#Override
public SessionFactoryBuilder getSessionFactoryBuilder(MetadataImplementor metadata, SessionFactoryBuilderImplementor defaultBuilder) {
this.metadata = metadata;
return defaultBuilder; //Just return the one provided in the argument itself. All we care about is the metadata :)
}
public static MetadataImplementor getMetadata() {
return metadata;
}
}
In order to register the above, create simple text file in the following path(assuming it's a maven project, ultimately we need the 'META-INF' folder to be available in the classpath):
src/main/resources/META-INF/services/org.hibernate.boot.spi.SessionFactoryBuilderFactory
And the content of the text file should be a single line(can even be multiple lines if you need to register multiple instances) stating the fully qualified class path of your implementation of SessionFactoryBuilderFactory. For example, for the above class, if your package name is 'com.yourcompany.prj', the following should be the content of the file.
com.yourcompany.prj.MetadataProvider
And that's it, if you run your application, spring app or standalone hibernate, you will have an instance of MetadataImplementor available through a static method once hibernate is bootstraped.
Update 1:
There is no way it can be injected via Spring. I digged into Hibernate's source code and the metadata object is not stored anywhere in SessionFactory(which is what we get from Spring). So, it's not possible to inject it. But there are two options if you want it in Spring's way:
Extend existing classes and customize all the way from
LocalSessionFactoryBean -> MetadataSources -> MetadataBuilder
LocalSessionFactoryBean is what you configure in Spring and it has an object of MetadataSources. MetadataSources creates MetadataBuilder which in turn creates MetadataImplementor. All the above operations don't store anything, they just create object on the fly and return. If you want to have an instance of MetaData, you should extend and modify the above classes so that they store a local copy of respective objects before they return. That way you can have a reference to MetadataImplementor. But I wouldn't really recommend this unless it's really needed, because the APIs might change over time.
On the other hand, if you don't mind building a MetaDataImplemetor from SessionFactory, the following code will help you:
EntityManagerFactoryImpl emf=(EntityManagerFactoryImpl)lcemfb.getNativeEntityManagerFactory();
SessionFactoryImpl sf=emf.getSessionFactory();
StandardServiceRegistry serviceRegistry = sf.getSessionFactoryOptions().getServiceRegistry();
MetadataSources metadataSources = new MetadataSources(new BootstrapServiceRegistryBuilder().build());
Metadata metadata = metadataSources.buildMetadata(serviceRegistry);
SchemaUpdate update=new SchemaUpdate(serviceRegistry,metadata); //To create SchemaUpdate
// You can either create SchemaExport from the above details, or you can get the existing one as follows:
try {
Field field = SessionFactoryImpl.class.getDeclaredField("schemaExport");
field.setAccessible(true);
SchemaExport schemaExport = (SchemaExport) field.get(serviceRegistry);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
Take a look on this one:
public class EntityMetaData implements SessionFactoryBuilderFactory {
private static final ThreadLocal<MetadataImplementor> meta = new ThreadLocal<>();
#Override
public SessionFactoryBuilder getSessionFactoryBuilder(MetadataImplementor metadata, SessionFactoryBuilderImplementor defaultBuilder) {
meta.set(metadata);
return defaultBuilder;
}
public static MetadataImplementor getMeta() {
return meta.get();
}
}
Take a look on This Thread which seems to answer your needs
Well, my go to on this:
public class SchemaTranslator {
public static void main(String[] args) throws Exception {
new SchemaTranslator().run();
}
private void run() throws Exception {
String packageName[] = { "model"};
generate(packageName);
}
private List<Class<?>> getClasses(String packageName) throws Exception {
File directory = null;
try {
ClassLoader cld = getClassLoader();
URL resource = getResource(packageName, cld);
directory = new File(resource.getFile());
} catch (NullPointerException ex) {
throw new ClassNotFoundException(packageName + " (" + directory + ") does not appear to be a valid package");
}
return collectClasses(packageName, directory);
}
private ClassLoader getClassLoader() throws ClassNotFoundException {
ClassLoader cld = Thread.currentThread().getContextClassLoader();
if (cld == null) {
throw new ClassNotFoundException("Can't get class loader.");
}
return cld;
}
private URL getResource(String packageName, ClassLoader cld) throws ClassNotFoundException {
String path = packageName.replace('.', '/');
URL resource = cld.getResource(path);
if (resource == null) {
throw new ClassNotFoundException("No resource for " + path);
}
return resource;
}
private List<Class<?>> collectClasses(String packageName, File directory) throws ClassNotFoundException {
List<Class<?>> classes = new ArrayList<>();
if (directory.exists()) {
String[] files = directory.list();
for (String file : files) {
if (file.endsWith(".class")) {
// removes the .class extension
classes.add(Class.forName(packageName + '.' + file.substring(0, file.length() - 6)));
}
}
} else {
throw new ClassNotFoundException(packageName + " is not a valid package");
}
return classes;
}
private void generate(String[] packagesName) throws Exception {
Map<String, String> settings = new HashMap<String, String>();
settings.put("hibernate.hbm2ddl.auto", "drop-create");
settings.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQL94Dialect");
MetadataSources metadata = new MetadataSources(
new StandardServiceRegistryBuilder()
.applySettings(settings)
.build());
for (String packageName : packagesName) {
System.out.println("packageName: " + packageName);
for (Class<?> clazz : getClasses(packageName)) {
System.out.println("Class: " + clazz);
metadata.addAnnotatedClass(clazz);
}
}
SchemaExport export = new SchemaExport(
(MetadataImplementor) metadata.buildMetadata()
);
export.setDelimiter(";");
export.setOutputFile("db-schema.sql");
export.setFormat(true);
export.execute(true, false, false, false);
}
}
please see my requirement: using SchemaExport to export database schema that appiled BeanValidation constraints(eg, #Length(32) will create DB constraints: column(32)).
In Hibernate 4.1.x, i can using the hack code post here: https://forum.hibernate.org/viewtopic.php?f=1&t=1024911&view=previous
but the Ejb3Configuration class that required in above hack code was removed in Hibernate 4.3.5.
so how can i export database schema that appiled BeanValidation constraints without using Ejb3Configuration?
Something like this should work:
PersistenceUnitDescriptorAdapter pu = new PersistenceUnitDescriptorAdapter() {
#Override
public List<String> getManagedClassNames() {
return Arrays.asList( MyClass.class.getName(), ... );
}
};
Map<Object, Object> settings = new HashMap<Object, Object>();
settings.put( "javax.persistence.schema-generation.scripts.action", "create" );
settings.put( "javax.persistence.schema-generation.scripts.create-target", "<path-to-export-file>" );
EntityManagerFactoryBuilderImpl factoryBuilder = new EntityManagerFactoryBuilderImpl( pu, settings );
factoryBuilder.generateSchema();
It relies on Hibernate internal classes, but so did your earlier solution. You could create a issue here - https://hibernate.onjira.com/browse/HHH - explaining your use case. Maybe a solution using a public API can be made available.
i found an temporary solution by using HibernationConfiguration build by EntityManagerFactoryBuilderImpl. it uses the JPA configuration to emit the schema script(with bean-validator constraints).
public final class JpaSchemaExporter
{
public JpaSchemaExporter(String utilName, String packageName, Properties properties, DialectType dialect,
Path outputPath) throws Exception
{
this.dialect = dialect;
this.outputPath = outputPath;
if (Files.exists(outputPath) && !Files.isDirectory(outputPath)) {
throw new IllegalArgumentException(
"Given path already exist and is not a directory! the path:" + outputPath);
}
Files.createDirectories(outputPath);
pud = new ParsedPersistenceXmlDescriptor(Resources.getResourceURL("META-INF"));
pud.setName(utilName);
pud.addClasses(Resources.getClasseNames(packageName));
pud.addMappingFiles("META-INF/orm.xml");
properties.setProperty("hibernate.dialect", dialect.getDialectClass());
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
factoryBuilder = new EntityManagerFactoryBuilderImpl( pud, properties );
factoryBuilder.withValidatorFactory(validatorFactory).build().close(); // create HibernateConfiguration instance
this.injectBeanValidationConstraintToDdlTranslator();
validatorFactory.close();
}
private void injectBeanValidationConstraintToDdlTranslator() {
try {
Configuration hibernateConfiguration = factoryBuilder.getHibernateConfiguration();
ValidatorFactory validatorFactory = (ValidatorFactory) factoryBuilder.getConfigurationValues().get(AvailableSettings.VALIDATION_FACTORY);
// private class in hibernate
Method applyRelationalConstraints = Class.forName("org.hibernate.cfg.beanvalidation.TypeSafeActivator")
.getMethod("applyRelationalConstraints",
ValidatorFactory.class,
java.util.Collection.class,
Properties.class,
Dialect.class);
applyRelationalConstraints.setAccessible(true);
Dialect dialectInstance = (Dialect) Class.forName(dialect.getDialectClass()).newInstance();
applyRelationalConstraints.invoke(null, validatorFactory,
Arrays.asList(Iterators.toArray(hibernateConfiguration.getClassMappings(), PersistentClass.class)) ,
hibernateConfiguration.getProperties(),
dialectInstance);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
#SuppressWarnings("unchecked")
public void create() throws IOException {
Configuration cfg = factoryBuilder.getHibernateConfiguration();
cfg.setProperty("hibernate.hbm2ddl.auto", "create");
SchemaExport export = new SchemaExport(cfg);
export.setDelimiter(";");
export.setOutputFile(Paths.get(outputPath.toString(), "ddl_create_" + dialect.name().toLowerCase() + ".sql").toString());
export.execute(true, false, false, true);
if (!export.getExceptions().isEmpty()) {
System.out.println();
System.out.println("SOME EXCEPTIONS OCCURED WHILE GENERATING THE UPDATE SCRIPT:");
for (Exception e : (List<Exception>) export.getExceptions()) {
System.out.println(e.getMessage());
}
}
}
#SuppressWarnings("unchecked")
public void update() throws IOException {
Configuration cfg = factoryBuilder.getHibernateConfiguration();
cfg.setProperty("hibernate.hbm2ddl.auto", "update");
SchemaUpdate updater = new SchemaUpdate(cfg);
updater.setDelimiter(";");
updater.setOutputFile(Paths.get(outputPath.toString(), "ddl_update_" + dialect.name().toLowerCase() + ".sql").toString());
updater.execute(true, false);
if (!updater.getExceptions().isEmpty()) {
System.out.println();
System.out.println("SOME EXCEPTIONS OCCURED WHILE GENERATING THE UPDATE SCRIPT:");
for (Exception e : ((List<Exception>) updater.getExceptions())) {
System.out.println(e.getMessage());
}
}
}
public void validate() {
Configuration hibernateConfiguration = factoryBuilder.getHibernateConfiguration();
hibernateConfiguration.setProperty("hibernate.hbm2ddl.auto", "validate");
SchemaValidator validator = new SchemaValidator(hibernateConfiguration);
validator.validate();
}
public static void main(String[] args) throws Exception {
Properties prop = new Properties(System.getProperties());
prop.setProperty("hibernate.connection.driver_class", "value in your env");
prop.setProperty("hibernate.connection.url", "value in your env");
prop.setProperty("hibernate.connection.username", "value in your env");
prop.setProperty("hibernate.connection.password", "value in your env");
Path path = Paths.get("schema output path in your env");
String packageName = prop.getProperty("package names of jpa classes");
String unitName = prop.getProperty("jpa Unit Name");
String[] dialects = "HSQL,MYSQL".split(",");
for(String dialect : dialects){
DialectType dialectType = DialectType.valueOf(dialect);
JpaSchemaExporter ddlExporter = new JpaSchemaExporter(unitName, packageName, prop, dialectType, path);
ddlExporter.update();
ddlExporter.create();
}
}
}
I am running a java app (java 1.7) with hibernate 4.2.0 and I need to implement a schema based multi tenancy setup. I used this exampleto do that.
my problem is that I was unable to figure out how to create the connection providers. the example uses:
acmeProvider = ConnectionProviderBuilder.buildConnectionProvider( "acme" );
jbossProvider = ConnectionProviderBuilder.buildConnectionProvider( "jboss" );
but ConnectionProviderBuilder is for testing use.
I tried to use the following:
C3P0ConnectionProvider connectionProvider = new C3P0ConnectionProvider()
{
public boolean supportsAggressiveRelease()
{
return allowAggressiveRelease;
}
};
connectionProvider.configure(props);
the problem here was that the C3P0ConnectionProvider has a null serviceRegistry which crush the with NPE.
does anyone have an idea on how to create ConnectionProvider for each tenant?
Thanks,
Ronen
Try suggestion using Properties to add data source properties:
DatasourceConnectionProviderImpl cp = new DatasourceConnectionProviderImpl();
cp.setDataSource(ds);
Properties p = new Properties();
// ...
cp.configure(p);
Or with DriverManagerConnectionProviderImpl:
DriverManagerConnectionProviderImpl connectionProvider = new DriverManagerConnectionProviderImpl() {
public boolean supportsAggressiveRelease() {
return allowAggressiveRelease;
}
};
connectionProvider.configure( props );