I have a hibernate class with more than one property, I have heard that NHibernate tracks properties in files and only updates those properties that have changed. Is there similar functionality in Hibernate?
I have tried to get it to work using just a simple class load - then set one property - then flush changes. However this without fail updates all the properties in the class.
You need to set the dynamic-update property to true via annotation or in the class mapping which would exclude unmodified properties in the Hibernate’s SQL update statement. You can find a good reference here. By default, the dynamic-update is set to false so as to be backward compatible as it is somewhat new feature introduced.
You can get Entity from db first then update ... try this example..
public final void updateEntity(EntityName entity){
//get entity from db by id
EntityName dbEntity = session.get(EntityName.class, item.getProductId());
//set property
dbEntity.setStatus(entity.getStatus());
//and update
session.update(dbEntity);
}
You can use the #DynamicUpdate annotation at the entity level, as shown here.
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.
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 have 2 Entitites, one maps to a database table, the other to a database view.
The data of the view depends on the table.
#javax.persistence.Table(name = "BOOKING_INFO", schema = "BOOKING")
#Entity
public class BookingInfo extends AbstractBooking {
#javax.persistence.Table(name = "BOOKING_VIEW", schema = "BOOKING")
#Entity
#Immutable
public class BookingView extends AbstractBooking {
This works fine in most cases, however when we write (insert or update) the Booking entity and then do queries (in my case a count) on the BookingView entity, we get stale data.
Why this happens is clear to me (hibernate caching, it only flushes when it detects that a select needs some data flushed).
So if I would do a query on the Booking entity, it would trigger a flush.
I have found the #Synchronize Annotation in Hibernate which sounds like it should fix this problem, like this:
#javax.persistence.Table(name = "BOOKING_VIEW", schema = "BOOKING")
#Entity
#Immutable
#Synchronize("BOOKING.BOOKING_INFO")
public class BookingView extends AbstractBooking {
However this does not make any difference (flush only happens at the end of the transaction). Also the documentation I have found about this annotation is quite lacking and not very helpful.
EDIT: I also tried #Synchronize("BOOKING_INFO") (without the schema name, and also lowercase, but that made no difference)
The docs say that it is mostly used with #Subselect but it is not a must (I don't want that).
Has anyone ever successfully used this annotation?
Is there any other way to handle database views in Hibernate?
Am I missing something else?
Thanks to a colleague we were able to debug and fix this, the problem was that our Hibernate naming-strategy lowercased our table-names, so the correct annotaiton is:
#Synchronize("BOOKING.booking_info")
How to debug this:
set breakpoints in Hibernates ActionQueue class in the areTablesToBeUpdated methods.
There we saw that it compared "BOOKING.BOOKING_VIEW" to "BOOKING.booking_view".
We think this is a bug in hibernate because it should either apply the naming-strategies also to the values from #Synchronize or compare these case-insensitive (which could theoretically lead to too many flushes if you have a crazy database which uses tables with the same name only differentiated by casing).
Created a Hibernate issue: https://hibernate.atlassian.net/browse/HHH-10002
I have an application which uses Spring and Hibernate. In my database there are some views that I need to load in some entities. So I'm trying to execute a native query and load the class withthe data retrieved from the view:
//In my DAO class (#Repository)
public List<MyClass> findMyEntities(){
Query query = em.createNativeQuery("SELECT * FROM V_myView", MyClass.class);
return query.getResultList();
}
and MyClass has the same fields as the column names of the view.
The problem is that Hibernate can't recognize MyClass because it's not an entity (it's not annotated with #Entity)
org.hibernate.MappingException: Unknown entity
If I put MyClass as an entity the system will put try to create/update a table for that entity, because I have configured it :
<property name="hibernate.hbm2ddl.auto" value="update"/>
So I come into these questions:
Can I disable "hibernate.hbm2ddl.auto" just for a single entity?
Is there any way to load the data from a view into a non-entity class?
If not, what would be the best way in my case for loading the data from a view into a class in hibernate?
Thanks
Placed on your class
#Entity
#Immutable
#Subselect(QUERY)
public MyClass {....... }
Hibernate execute the query to retrieve data, but not create the table or view. The downside of this is that it only serves to make readings.
You may use axtavt solution. You may also just execute your query, and transform the List<Object[]> it will return into a List<MyClass> explicitely. Or you may map your view as a read-only entity, which is probably the best solution, because it would allow for associations with other tables, querying through JPQL, Criteria, etc.
In my opinion, hibernate.hbm2ddl.auto should only be used for quick n' dirty prototypes. Use the hibernate tools to generate the SQL file allowing to create the schema, and modify it to remove the creation of the view. Anyway, if it's set to update, shouldn't it skip the table creation since it already exists (as a view)?
You can use AliasToBeanResultTransformer. Since it's a Hibernate-specific feature, you need to access the underlying Hibernate Session:
return em.unwrap(Session.class)
.createSQLQuery("...")
.setResultTransformer(new AliasToBeanResultTransformer(MyClass.class))
.list();
So I'm still pretty new to Hibernate, and I'm working on a large-ish application that already has a database with several Hibernate tables. I'm working on a new feature, which includes a new #Entity class, and I need these objects to be stored in a new table. The class is declared like this:
#Entity
#Table(name="DATA_REQUEST")
public class DataRequest {
//Some fields, nothing fancy
}
The DATA_REQUEST table does not exist, nor do I have any data to store in it yet. I started the application up, expecting that it would either create the table or crash because it doesn't exist yet. Neither of these actually happened.
So: do I need to create the table manually (easily done)? Or do I need to go somewhere else to tell Hibernate that I need this table? I've seen the hibernate.cfg.xml file, which looks like a good place to start.
You need to specify "create" for the "hibernate.hbm2ddl.auto" property. Read more details here. This is not recommended in production but only for testing purposes.
As for adding a new column to the table
As long as it is not a not null column you don't need drop the table or restart your hibernate app
If you do want to use the column then you need to map the column in the code/hbm file, so you will have to restart the hibernate app
If there is no mapping present as far as hibernate is concerned the column does not exisist, If it is a not null column then underlying data base would reject inserts/updates as hibernate will not include the column in generated sql
from hibernate documentation
hibernate.hbm2ddl.auto
Automatically validates or exports schema DDL to the database when the SessionFactory is created. With create-drop, the database schema will be dropped when the SessionFactory is closed explicitly.
e.g. validate | update | create | create-drop
hibernate Configuration