In a spring mvc app using hibernate, jpa, and MySQL, I have a BaseEntity that contains an id field that is unique across all classes that inherit from BaseEntity, using #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS). Some data is imported into the MySQL database using an external dml.sql file run from the command line. The imported data is carefully planned so that all the ids that need to be managed as part of the BaseEntity inheritance group are unique within their inheritance group.
The problem is that hibernate is not taking the values of the ids already in the database into account when it inserts a new record into the database. Instead, hibernate is saving an id value in one of the descendent entities which is identical to an id stored in one of the other descendent entities.
How can I configure hibernate to respect the id values already in the database when it saves a new entity within the same inheritance group?
Some relevant facts are:
All of the objects in the MySQL database were created directly from the hibernate mappings in the app by using hbm2ddl.
I cannot use #MappedSuperClass for BaseEntity because BaseEntity is used as a property of one of the entities in the app, so that entities of various types can be stored in the same property of that entity. When I was using #MappedSuperClass, eclipse was giving compile errors saying that BaseEntity cannot be instantiated directly because it has #MappedSuperClass annotation.
Note: The file sharing site seems to be center-justifying all the code. You can fix this by simply cutting and pasting it into a text editor.
You can read the code for BaseEntity by clicking on this link.
The code for the entity whose id values are being set incorrectly by hibernate can be read by clicking on this link.
The jpql code for saving the entity whose id is being set incorrectly is as follows:
#Override
#Transactional
public void saveCCD(HL7ConsolidatedCareDocument ccd) {
if (ccd.getId() == null) {
this.em.persist(ccd);
this.em.flush();
}
else {
this.em.merge(ccd);
this.em.flush();
}
}
I have never done this using hibernate or mysql ut have done something similar with EclipseLink + PostgreSQL. So there might be some mistakes below.
With generation type TABLE you might want to explicitly specify some additional parameters using the TableGenerator annotation. That way you are certain where hibernate is storing things.
#Id
#GeneratedValue(
strategy=GenerationType.TABLE,
generator="TBL_GEN")
#javax.persistence.TableGenerator(
name="TBL_GEN",
table="GENERATOR_TABLE",
pkColumnName = "mykey",
valueColumnName = "hi"
pkColumnValue="BaseEntity_Id",
allocationSize=20
)
What you need to do when you bypass hibernate is to reserve the ids you need by updating the row with mykey BaseEntity_Id in the table GENERATOR_TABLE.
For details on the annotations see paragraph 5.1.2.2
Related
The only link I found that's close to what I am experiencing is this one :
How do you synchronize the id of a java object to its associated db row?
and there's not much of a solution in it.
My problem is that my Java objects aren't updated after being added to my database despite the .commit()
em.getTransaction().begin();
System.out.println(eleve.getID());
em.persist(eleve);
em.getTransaction().commit();
System.out.println(eleve.getID());
which refers to this class
public class Eleve {
private String _nom;
private String _prenom;
private float _ptsMerite;
#Id
private int _IDEleve;
and yields this output :
0
0
I think I've done everything properly when it comes to the persistence since it does create the object in the database (mySQL) with correct ID's which I've set to be autoincrement.
I am using javax.persistence for everything (annotations and such).
Did you try to add the #GeneratedValue annotation at your ID field?
There are four possible strategies you can choose from:
GenerationType.AUTO: The JPA provider will choose an appropriate strategy for the underlying database.
GenerationType.IDENTITY: Relies on a auto-increment column in your database.
GenerationType.SEQUENCE: Relies on a database sequence
GenerationType.TABLE: Uses a generator table in the database.
More info: https://www.baeldung.com/jpa-strategies-when-set-primary-key
If you ever change to a more powerful framework it is likely that this manages your transactions (CMT) so you can't (or don't want) commit everytime you want to access the ID for a new entity. In these cases you can use EntityManager#flush to synchronize Entity Manager with database.
I'm a java developer. I'm using spring 4.0.1 and hibernate 4.2.21. I have a class as follow:
#Entity
#Inheritance(...)
public abstract class Feature{
#Id
#GeneratedValue
protected Long id;
...
}
Now I have some many class as follow:
Label.java class:
#Entity
public class Label extends Feature{
protected String str;
...
}
Point.java class:
#Entity
public class Point extends Feature{
protected Integer intg;
...
}
I have more than 20 Entity class that extends from Feature class. Is there any way to add dynamically this classes(such as Label and Point) to the project without writing hard code?
update:
For example, Hibernate get data from a database and then according this data, create models.
Is it possible?
How do I do?
I think its not a good database design that needs to be changed dynamically. It sounds verbose and not consistent. Observe your domain again and try to design a proper entity relationships that wouldnt be changed over run time.
You can try to collect the needed data to build the model and generate a hibernate hbm.xml file for each entity (is xml format and easy to generate with java after reading the data needed as you describe in your update)
After that, you can create programmatically a hibernate configuration object following this http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/session-configuration.html#configuration-programmatic
I Think with that approach you can achieve what you want if I understand well your question.
I think you want to generate your entity class at runtime instead of that you have to write your java file and compile it and so on.
If this is your requirement you can use a byte code generator like javassist to generate and annotate your class file at runtime. Then you can persist it to your table using JPA, Hibernate and any other ORM framework.
As I understand you need to develop a tool, collects table names that have one-to-one relationship with Feature table.
My suggestion is like that (tested with Oracle):
1) From your DB, get tables metadata who is referancing your Feature table.
Below will print your Label, Point, etc tables who has foreign key relation to your table.If you want to only generate a subset (irrelevant tables might has this relationship too) may be you put a common foreign key column name and filter out non-related tables with a help of such marking.
Connection connection = jdbcTemplate.getDataSource().getConnection();
DatabaseMetaData metaData = connection.getMetaData();
ResultSet exportedKeys = metaData.getExportedKeys(connection.getCatalog(), "<your_schema_name>", "FEATURE");
while (exportedKeys.next()){
String fkTableName = exportedKeys.getString("FKTABLE_NAME");
String fkColumnName = exportedKeys.getString("FKCOLUMN_NAME");
System.out.println("[fkTableName:" + fkTableName + "], [fkColumnName" + fkColumnName + "]");
}
exportedKeys.close();
2) For the tables you collected above, for each table of our concern, get table metadata for the types and columns.
ResultSet columns = metaData.getColumns(connection.getCatalog(), "<your_schema_name>", "<your_table_name>", null);
while (columns.next()){
String columnName = columns.getString("COLUMN_NAME");
String typeName = columns.getString("TYPE_NAME");
System.out.println("[columnName:" + columnName + "], [typeName" + typeName + "]");
}
columns.close();
3) According to result from 2 generate your Java classes. With fields, getter setters, annotations etc. Then copy them into your source directory. You know the rest :)
Hope this is helpful.
I think you can use Hibernate Reverse Engineering to generate Entity for all the database tables. Please refer this Link. That will explained step by step process to generate entity from database using hibernate reverse engineering.
Do not repeat yourself.
If you really need those classes use an IDE (like eclipse) to generate the classes. Or use generics and inheritance to create only one class that is capable of storing Strings as well as Integers.
But if you do not actually need classes, generate SQL (not JPQL nor HQL) and to store the data in java.util.Map and similar data structures.
Classes are good for:
type safety
combining logic (methods) with data (fields)
describing relationships
In your case you might only need:
store structured data at runtime.
I think you could do this with eclipse, but the classes had to be modified more or less to preserve the inheritance hierarchy.
Righ click on the project name and select Properties
Use project facets if project facets not enabled
Click the JPA if it's not selected, then click OK to close the project properties window.
After enabling JPA in project properties, now right click you eclipse project name again, you should see a new context menu item JPA tools appears. Choose Generate Entities from tables
Select a database connection to let Eclipse get the tables used to generated
class from.
Here is how to setup db in eclipse
It's better to create the entities in a dummy project using the above method and copy the Entity classes to the real project.
Eclipse's Class refactoring may be used to preserve the inheritance hierarchy that you want.
Hope this helps.
I'm developing a code generator that have to generate JPA entities from database meta-model files. These model are from home-brewed modeling system which are being used to generate models other than JPA entities.
In these models some fields are mapping back to same database column. But it seems like JPA does not like that very much. When I try to run generated code I get
Exception [EclipseLink-48] (Eclipse Persistence Services - 2.6.0.v20140809-296a69f): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Multiple writable mappings exist for the field [FACT_INVENT_TRANS_HIST_DM.TRANSACTION_ID]. Only one may be defined as writable, all others must be specified read-only.
Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[TransactionIdKey-->FACT_INVENT_TRANS_HIST_DM.TRANSACTION_ID]
Descriptor: RelationalDescriptor(InventTransHistFactDM --> [DatabaseTable(FACT_INVENT_TRANS_HIST_DM)])
As I can't change the models only option left is to make one of those fields read-only. And the JPA entities being generated are only used to read data from database it will not used for writing data. Is there a way to mark some fields as read only or tell EclipseLink that these entities are read only so it does not have to worry about the multiple writable mapping.
I tried using EclipseLink's #ReadOnly annotation in all entities but it did not help this issue.
There is no #ReadOnly in JPA.
There are however attributes "insertable"/"updatable" that you can set against a field via #Column to effectively do the same.
The question may be almost 6 years old, but it's still being found today, so I'd like to address another option:
public class Foobar {
#OneToOne
#JoinColumn(name="SELF_COLUMN_FOO", referencedColumnName = "FOREIGN_COLUMN_TO_JOIN")
public Foo foo;
#OneToOne
#JoinColumn(name="SELF_COLUMN_BAR", referencedColumnName = "FOREIGN_COLUMN_TO_JOIN")
public Bar bar;
}
This can be used where SELF_COLUMN is obviously the relevant column in the Foobar table, and FOREIGN_COLUMN_TO_JOIN would be single key in the other table you wish to join.
This will be useful where you want to have two (or more) attributes in a single class, but only one column to join on the foreign DB table. For example: An Employee may have a home phone number, cell number, and a work phone number. All are mapped to different attributes in the class, but on the database there's a single table of phone numbers and id's, and an identifier column, say VARCHAR(1) with 'H' or 'W' or 'C'. The real example would then be...
Tables:
PHONENUMBERS
PHONENUMBER_ID,
ACTUAL_NUMBER
EMPLOYEE
ID
HOMENUMBER VARCHAR(12),
CELLNUMBER VARCHAR(12),
WORKNUMBER VARCHAR(12)
public class Employee {
#OneToOne
#JoinColumn(name="HOMENUMBER", referencedColumnName = "PHONENUMBER_ID")
public Phone homeNum;
#OneToOne
#JoinColumn(name="CELLNUMBER", referencedColumnName = "PHONENUMBER_ID")
public Phone cellNum;
#OneToOne
#JoinColumn(name="WORKNUMBER", referencedColumnName = "PHONENUMBER_ID")
public Phone workNum;
}
As you can see, this would require multiple columns on the Entity's table, but allows you to reference a foreign key multiple times without throwing the 'Multiple writable mappings exist...' that you showed above. Not a perfect solve, but helpful for those encountering the same problem.
I am using hibernate retrieve results from my MySQL database into my Java project. Recently, I had a lot of redundant data and had to manually clean up the database by copying the required data into new tables and then renaming the newly created table to old table.
But, now querying the database with hibernate gives only one row as the result. I have manually checked the database and there are several different rows in the database. My query to Hibernate is something like this:
Criteria c = session.createCriteria(UserDto.class);
c.setMaxResults(100);
List<UserDto> users = c.list();
users contains 100 elements but all are the same.
The mapping of userDto is here.
Any idea what is happening here?
If your UserDto class has ToMany relations, then this is quite possible that outer join on them results in many records which all contain one and the same user data. You should use
session.createCriteria(UserDto.class).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
Double-check your mapping of the UserDto class to the database.
My guess is that you don't have it mapped to the table that you think you do.
As Sebastien mentioned, setting hibernate.show.sql to true should make this obvious.
Did you deleted the old tables? And in the configuration file what is the value for "hibernate.hbm2ddl.auto"?
I think the reason is these records have same id, so Hibernate treat them as the same record. You can check it.
I had same problem. In my case, the problem detected when I created a table in MySQL manually and I tried to read data from that table using hibernate and a dto class. After checking my dto class fields and database table, I figured out that there is a difference between table column named "id" and the class field which named dbId. The code was something like this:
#Id
#GeneratedValue
#Column(name="db_id", unique = true)
private long dbId;
So I edited the name and changed the code:
#Id
#GeneratedValue
#Column(name="id", unique = true)
private long dbId;
Which "id" was the correct name of databse table column and the problem has been solved.
I have a couple of objects that are mapped to tables in a database using Hibernate, BatchTransaction and Transaction. BatchTransaction's table (batch_transactions) has a foreign key reference to transactions, named transaction_id.
In the past I have used a batch runner that used internal calls to run the batch transactions and complete the reference from BatchTransaction to Transaction once the transaction is complete. After a Transaction has been inserted, I just call batchTransaction.setTransaction(txn), so I have a #ManyToOne mapping from BatchTransaction to Transaction.
I am changing the batch runner so that it executes its transactions through a Web service. The ID of the newly inserted Transaction will be returned by the service and I'll want to update transaction_id in BatchTransaction directly (rather than using the setter for the Transaction field on BatchTransaction, which would require me to load the newly inserted item unnecessarily).
It seems like the most logical way to do it is to use SQL rather than Hibernate, but I was wondering if there's a more elegant approach. Any ideas?
Here's the basic mapping.
BatchQuery.java
#Entity
#Table(name = "batch_queries")
public class BatchQuery
{
#ManyToOne
#JoinColumn(name = "query_id")
public Query getQuery()
{
return mQuery;
}
}
Query.java
#Entity
#Table(name = "queries")
public class Query
{
}
The idea is to update the query_id column in batch_queries without setting the "query" property on a BatchQuery object.
Using a direct SQL update, or an HQL update, is certainly feasible.
Not seeing the full problem, it looks to me like you might be making a modification to your domain that's worth documenting in your domain. You may be moving to having a BatchTransaction that has as a member just the TransactionId and not the full transaction.
If in other activities, the BatchTransaction will still be needing to hydrate that Transaction, I'd consider adding a separate mapping for the TransactionId, and having that be the managing mapping (make the Transaction association update and insert false).
If BatchTransaction will no longer be concerned with the full Transaction, just remove that association after adding a the TransactionId field.
As you have writeen, we can use SQL to achieve solution for above problem. But i will suggest not to update the primary keys via SQL.
Now, as you are changing the key, which means you are creating alltogether a new object, for this, you can first delete the existing object, with the previous key, and then try to insert a new object with the updated key(in your case transaction_id)