I have a next problem: I ve got a several tables in database, and somehow i need to make a query from one of them, but only in runtime i will know the name of needed table, so is any possibility to pass table name to #Table annotation of entity in runtime? All of these tables have absolutely identical schema. The only distinction - is name (BILLING_DATA_2016_1, BILLING_DATA_2015_12 etc.). Do somebody have an idea how can i pass the name of table dynamically? Or may be some hack with Queries and Inheritance?
You can try HibernateInterceptor. Place the {BILLING_DATA_PLACEHOLDER} string into your #Table annotation and just replace it on fly
public class HibernateInterceptor extends EmptyInterceptor {
#Override
public String onPrepareStatement(String sql) {
String prepedStatement = super.onPrepareStatement(sql);
prepedStatement = prepedStatement.replaceAll("{BILLING_DATA_PLACEHOLDER}", theRealSchemaName);
return prepedStatement;
}
}
Have not tried though
Related
I'm working on a Spring Boot application that uses JPA (Hibernate) for the persistence layer.
I'm currently implementing a migration functionality. We basically dump all the existing entities of the system into an XML file. This export includes ids of the entities as well.
The problem I'm having is located on the other side, reimporting the existing data. In this step the XML gets transformed to a Java object again and persisted to the database.
When trying to save the entity, I'm using the merge method of the EntityManager class, which works: everything is saved successfully.
However when I turn on the query logging of Hibernate I see that before every insert query, a select query is executed to see if an entity with that id already exists. This is because the entity already has an id that I provided.
I understand this behavior and it actually makes sense. I'm sure however that the ids will not exist so the select does not make sense for my case. I'm saving thousands of records so that means thousands of select queries on large tables which is slowing down the importing process drastically.
My question: Is there a way to turn this "checking if an entity exists before inserting" off?
Additional information:
When I use entityManager.persist() instead of merge, I get this exception:
org.hibernate.PersistentObjectException: detached entity passed to
persist
To be able to use a supplied/provided id I use this id generator:
#Id
#GeneratedValue(generator = "use-id-or-generate")
#GenericGenerator(name = "use-id-or-generate", strategy = "be.stackoverflowexample.core.domain.UseIdOrGenerate")
#JsonIgnore
private String id;
The generator itself:
public class UseIdOrGenerate extends UUIDGenerator {
private String entityName;
#Override
public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
entityName = params.getProperty(ENTITY_NAME);
super.configure(type, params, serviceRegistry);
}
#Override
public Serializable generate(SessionImplementor session, Object object)
{
Serializable id = session
.getEntityPersister(entityName, object)
.getIdentifier(object, session);
if (id == null) {
return super.generate(session, object);
} else {
return id;
}
}
}
If you are certain that you will never be updating any existing entry on the database and all the entities should be always freshly inserted, then I would go for the persist operation instead of a merge.
Per update
In that case (id field being set-up as autogenerated) the only way would be to remove the generation annotations from the id field and leave the configuration as:
#Id
#JsonIgnore
private String id;
So basically setting the id up for always being assigned manually. Then the persistence provider will consider your entity as transient even when the id is present.. meaning the persist would work and no extra selects would be generated.
I'm not sure I got whether you fill or not the ID. In the case you fill it on the application side, check the answer here. I copied it below:
Here is the code of Spring SimpleJpaRepository you are using by using Spring Data repository:
#Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
It does the following:
By default Spring Data JPA inspects the identifier property of the given entity. If the identifier property is null, then the entity will be assumed as new, otherwise as not new.
Link to Spring Data documentation
And so if one of your entity has an ID field not null, Spring will make Hibernate do an update (and so a SELECT before).
You can override this behavior by the 2 ways listed in the same documentation. An easy way is it to make your Entity implement Persistable (instead of Serializable), which will make you implement the method "isNew".
When I use IntelliJ to generate a persistence mapping from exisitng database schema it puts a catalog value as part of #Table annotation. Unfortunately names of database instances have names of dev/test/prod environemnts in them and while I can overwrite the connection string with a map passed to EntityManagerFactory I still get Invalid object name 'BAR_DEV.dbo.FOO' when executing a query against BAR_TEST instance.
Can I dynamically overwrite the catalog value at runtime without doing global search and replace to remove it manually after entity generation?
#Entity
#Table(name = "FOO", schema = "dbo", catalog = "BAR_DEV")
public class Foo{ /* ... */ }
No, it is not possible directly with standard JPA.
However, a solution I used in my project was to define multiple persistence units, each for a particular environment. You may overwrite any database mapping in an orm.xml file, or even set default catalog or schema for all entities. Next step is to dynamically retrieve proper EntityManager - if you are using Java EE, I recomment injecting using #Inject and creating a producer, which returns particular EM for specified environment.
Non portable, Eclipselink only org.eclipse.persistence.dynamic.DynamicHelper.SessionCustomizer can replace many defaults at runtime.
EDIT: I haven't ready code for You. I use this way
public void customize(Session session) throws SQLException {
...
for (ClassDescriptor descriptor : session.getDescriptors().values()) {
if (!descriptor.getTables().isEmpty() && descriptor.getAlias().equalsIgnoreCase(descriptor.getTableName())) {
tableName = TABLE_PREFIX + clazz.getSimpleName();
descriptor.setTableName(tableName);
}
}
I'm trying to create a simple login based on the Zentask sample, however I'm getting a runtime exception in smgts2\app\controllers\Application.java at line 43.
public static Result authenticate() {
Form<Login> loginForm = form(Login.class).bindFromRequest(); //Line 43
if(loginForm.hasErrors()) {
return badRequest(login.render(loginForm));
} else {
session("user_name", loginForm.get().user_name);
return redirect(
I've uploaded the files in github: https://github.com/gscruz/smgts2-start
Looking at your project on GitHub, I think the main issues are with the JPA annotations on your Accounts model class that models your user_account database table. The mappings on your model class get exercised when you bind the form data to a Login object, since Login.validate queries user_account.
Since the name of your class does not match the name of the table, you'll need a JPA #Table annotation to explicitly state the mapping:
#Entity
#Table(name = "user_account")
public class Accounts extends Model
You'll also need #Column annotations for the fields whose names don't match up with their corresponding columns. Give that a go and see if it gets you any further.
I have some tables in db(postgresql) with names like this "Test".When i try create java classes from this tables with hibernate its not happening. I get classes from tables with names like this test. How to make hibernate can see tables with quotes in names?
UPDATE
Maybe i write question not correct. But i cant create java classes and i want to know how to do reverse ingenering with tables which have names in qoutes. I cant delete qoutes from table names and column names couse they have names like Type and Full.
By default, Hibernate assumes the database table name is the same as the class name, but you can override this behaviour via the #Table annotation:
#Entity
#Table(name="\"Test\"") // Will use "Test" (including the quotes) as the table name
public class Test {
The #Table annotation is used to specify the table to persist the data. The name attribute refers to the table name. If #Table annotation is not specified then Hibernate will by default use the class name as the table name. So your database's table name is "Test" then you should use your class name is "Test".
Please check your database with
select * from """Test""" if your table name is "Test".
Your entity class should be
#Entity
#Table(name = "\"\"\"Test\"\"\"")
public class Test {
}
It seems that you are not using annotations as of now...
So in case you want to use "Test" as table name you should define the mapping of POJO with your table either via annotations as defined by Bohemian
#Entity
#Table(name="\"Test\"")
public class Test {
or define in the Test.hbm.xml in which you have to map your table and fields to java class and columns.
Alternately, you can specify the schema and database name inside #Table annotation.
#Entity
#Table(name = "Test", schema = "public", catalog = "TestDatabase")
Hibernate will recognize the table without the need to escape double quotes.
In our company we have a strange database model which can't be modified because to many systems works with them. Up to know we have a straight java application which connects with hibernate to the database and loads the data. We have for each table one xml mapping file.
The strange thing about the database is that we do not have any primary keys. Most table have a unique index containing several columns.
Now we want to use an application server (jboss) and the ejb model. So I created a class like this:
#Entity
#Table (name = "eakopf_t")
public class Eakopf implements Serializable {
#Embeddable
public static class EakopfId implements Serializable {
private String mandant;
private String fk_eakopf_posnr;
// I removed here the getters and setters to shorten it up
}
#Id
private EakopfId id;
private String login;
// I removed the getters and setters here as well
}
This works perfect.
Because our customers have different versions of the database schema I thought about extending this class on each database release change. So each interface we create with java can decide which version of the table will be used.
Here is the extended table class
#Entity
#Table (name = "eakopf_t")
public class Eakopf6001 extends Eakopf implements Serializable {
private String newField;
// getters and setters
}
If I use Eakopf (the base version) it is working if I do something like that:
EakopfId id = new EakopfId();
id.setMandant("001");
id.setFk_eakopf_posnr("ABC");
Eakopf kopf = (Eakopf) em.find(Eakopf.class, id);
But if I do this:
EakopfId id = new EakopfId();
id.setMandant("001");
id.setFk_eakopf_posnr("ABC");
Eakopf6001 kopf = (Eakopf6001) em.find(Eakopf6001.class, id);
this exception occues
javax.ejb.EJBException: javax.persistence.PersistenceException:
org.hibernate.WrongClassException: Object with id:
de.entity.Eakopf$EakopfId#291bfe83 was not of the specified subclass:
de.entity.Eakopf (Discriminator: null)
Does anybody has an idea?
many greetings,
Hauke
Doing what you did means to Hibernate that you're storing two different kinds of entities in a single table. This is possible is you use a discriminator column. But if I understand correctly, you just want one kind of entity in the table : Eakopf6001. In this case, its base class should be annotated with #MappedSuperClass, not with #Entity.
I would suggest creating a class annotated with #MappedEntity (let's call it BaseEakopf), and two entities: EaKopf and EaKopf6001, each with their set of additional fields. Include one of the other of the entities in the list of mapped classes, depending on which one you want to use.
My personal opinion is that if you have multiple versions of your app, they should use the same entities, but with different fields. Your version control system would take care of these multiple versions, rather than your source code (i.e. have one set of source files per version of the app, rather than one single set of source files for all the possible versions).