I have an Hibernate object as follows:
#Entity
#Table(name="SOME_TABLE")
public class SomeEntity {
private Long id;
private String someInfo;
#Id
#Column(name = "ID")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "SOME_INFO")
public String getSomeInfo() {
return someInfo;
}
public void setSomeInfo(String someInfo) {
this.someInfo = someInfo;
}
}
When loading the object using the following code:
sessionFactory.getCurrentSession().load(getEntityClass(), id);
The object's fields are not loaded, instead a proxy object is returned, and the actual fields are loaded only when I explicitly call them by their getter method.
To the best of my knowledge, plain fields (primitives, strings) should be loaded eagerly. Why does the fields, which are not relations or Collections are loaded lazily? is there any way to ask Hibernate to load them eagerly?
This is problematic for me as I use this object as the return value of a Spring REST application, and then I get a could not initialize proxy - no Session exception.
The reason you obtain a proxy is because the Session#load contract is permitted to return a proxy as a placeholder without ever querying the database for the specified object. This is also why it's crucial that the provided identifier for which you wish to load exists as you'll run into unexpected ObjectNotFoundException errors later on if so.
What you want to use is Session#get which is guaranteed to query the database and will not return a proxy, thus those basic attributes you mentioned will be eagerly loaded as you would expect.
For example:
final Comment comment = new Comment( "This is a comment" );
comment.setOwner( session.load( Product.class, productId ) );
session.save( comment );
The benefit here is that the Product isn't fully initialized. We create a persistent proxy with the specified productId value and associate it as the owner of the comment. This is sufficient when we persist the new Comment to make the foreign-key relationship occur without having to actually load the state of Product, avoiding unnecessary overhead.
Related
So I have looked at various tutorials about JPA with Spring Data and this has been done different on many occasions and I am no quite sure what the correct approach is.
Assume there is the follwing entity:
package stackoverflowTest.dao;
import javax.persistence.*;
#Entity
#Table(name = "customers")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
public Customer(String name) {
this.name = name;
}
public Customer() {
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
We also have a DTO which is retrieved in the service layer and then handed to the controller/client side.
package stackoverflowTest.dto;
public class CustomerDto {
private long id;
private String name;
public CustomerDto(long id, String name) {
this.id = id;
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
So now assume the Customer wants to change his name in the webui - then there will be some controller action, where there will be the updated DTO with the old ID and the new name.
Now I have to save this updated DTO to the database.
Unluckily currently there is no way to update an existing customer (except than deleting the entry in the DB and creating a new Cusomter with a new auto-generated id)
However as this is not feasible (especially considering such an entity could have hundreds of relations potentially) - so there come 2 straight forward solutions to my mind:
make a setter for the id in the Customer class - and thus allow setting of the id and then save the Customer object via the corresponding repository.
or
add the id field to the constructor and whenever you want to update a customer you always create a new object with the old id, but the new values for the other fields (in this case only the name)
So my question is wether there is a general rule how to do this?
Any maybe what the drawbacks of the 2 methods I explained are?
Even better then #Tanjim Rahman answer you can using Spring Data JPA use the method T getOne(ID id)
Customer customerToUpdate = customerRepository.getOne(id);
customerToUpdate.setName(customerDto.getName);
customerRepository.save(customerToUpdate);
Is's better because getOne(ID id) gets you only a reference (proxy) object and does not fetch it from the DB. On this reference you can set what you want and on save() it will do just an SQL UPDATE statement like you expect it. In comparsion when you call find() like in #Tanjim Rahmans answer spring data JPA will do an SQL SELECT to physically fetch the entity from the DB, which you dont need, when you are just updating.
In Spring Data you simply define an update query if you have the ID
#Repository
public interface CustomerRepository extends JpaRepository<Customer , Long> {
#Query("update Customer c set c.name = :name WHERE c.id = :customerId")
void setCustomerName(#Param("customerId") Long id, #Param("name") String name);
}
Some solutions claim to use Spring data and do JPA oldschool (even in a manner with lost updates) instead.
Simple JPA update..
Customer customer = em.find(id, Customer.class); //Consider em as JPA EntityManager
customer.setName(customerDto.getName);
em.merge(customer);
This is more an object initialzation question more than a jpa question, both methods work and you can have both of them at the same time , usually if the data member value is ready before the instantiation you use the constructor parameters, if this value could be updated after the instantiation you should have a setter.
If you need to work with DTOs rather than entities directly then you should retrieve the existing Customer instance and map the updated fields from the DTO to that.
Customer entity = //load from DB
//map fields from DTO to entity
So now assume the Customer wants to change his name in the webui -
then there will be some controller action, where there will be the
updated DTO with the old ID and the new name.
Normally, you have the following workflow:
User requests his data from server and obtains them in UI;
User corrects his data and sends it back to server with already present ID;
On server you obtain DTO with updated data by user, find it in DB by ID (otherwise throw exception) and transform DTO -> Entity with all given data, foreign keys, etc...
Then you just merge it, or if using Spring Data invoke save(), which in turn will merge it (see this thread);
P.S. This operation will inevitably issue 2 queries: select and update. Again, 2 queries, even if you wanna update a single field. However, if you utilize Hibernate's proprietary #DynamicUpdate annotation on top of entity class, it will help you not to include into update statement all the fields, but only those that actually changed.
P.S. If you do not wanna pay for first select statement and prefer to use Spring Data's #Modifying query, be prepared to lose L2C cache region related to modifiable entity; even worse situation with native update queries (see this thread) and also of course be prepared to write those queries manually, test them and support them in the future.
I have encountered this issue!
Luckily, I determine 2 ways and understand some things but the rest is not clear.
Hope someone discuss or support if you know.
Use RepositoryExtendJPA.save(entity). Example:
List<Person> person = this.PersonRepository.findById(0)
person.setName("Neo");
This.PersonReository.save(person);
this block code updated new name for record which has id = 0;
Use #Transactional from javax or spring framework. Let put #Transactional upon your class or specified function, both are ok. I read at somewhere that this annotation do a "commit" action at the end your function flow. So, every things you modified at entity would be updated to database.
There is a method in JpaRepository
getOne
It is deprecated at the moment in favor of
getById
So correct approach would be
Customer customerToUpdate = customerRepository.getById(id);
customerToUpdate.setName(customerDto.getName);
customerRepository.save(customerToUpdate);
I'm facing LazyInitializationException when I'm trying to access ID of a lazy #ManyToOne reference of a detached entity. I do not want to fetch the refrence completely, but just need the ID (which should be exist in original object in order to fetch refrence in a lazy/deferred manner).
EntityA ea = dao.find(1) // find is #Transactional, but transaction is closed after method exits
ea.getLazyReference().getId() // here is get exception. lazyReference is a ManyToOne relation and so the foreight key is stored in EntityA side.
To paraphrase, how can I access ID of LazyReference (which actually exists in initial select for EntityA) without actually fetching the whole LazyReference?
When field access is used, Hibernate treats getId() method the same as any other method, meaning that calling it triggers proxy initialization, thus leading to LazyInitializationException if invoked on a detached instance.
To use property access only for id property (while keeping field access for all the other properties), specify AccessType.PROPERTY for the id field:
#Entity
public class A {
#Id
#Access(AccessType.PROPERTY)
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
That should be possible. I am able to get only the ID of the #ManyToOne LAZY entity.
But for that I have set annotations on the getters of the entity instead of setting them directly on the instance variables which results in null value.
I believe you are using annotations on the instance variables. You can try getter annotations and see if that helps you.
You get an LazyInitializationException exception, because of Hibernate wraps your persistent with a proxy object. A proxy generates an exception for any getter of a lazy object even for id that LazyReference already has of course.
To get id without LazyInitializationException you can use this method (you can refer the link for other interesting utilite methods)
#SuppressWarnings("unchecked")
public static <T> T getPid(Persistent<?> persistent) {
if (persistent == null) {
return null;
}
if (!(persistent instanceof HibernateProxy) || Hibernate.isInitialized(persistent)) {
return (T) persistent.getPid();
}
LazyInitializer initializer = ((HibernateProxy) persistent).getHibernateLazyInitializer();
return (T) initializer.getIdentifier();
}
Persistent is a base class for all persistents. For your LazyReference you can rewrite code like this
#SuppressWarnings("unchecked")
public static Long getId(LazyReference persistent) {
if (persistent == null) {
return null;
}
if (!(persistent instanceof HibernateProxy) || Hibernate.isInitialized(persistent)) {
return persistent.getId();
}
LazyInitializer initializer =
((HibernateProxy) persistent).getHibernateLazyInitializer();
return initializer.getIdentifier();
}
I have a form to fill a POJO called Father. Inside it, I have a FotoFather field.
When I save a new Father, I save automatically the object FotoFather (with Hibernate ORM pattern).
FotoFather.fotoNaturalUrl must be filled with the value of Father.id and here is the problem!
When i'm saving Father on the db, of course I still haven't Father.id value to fill FotoFather.fotoNaturalUrl. How can I solve this problem?
Thank you
#Entity
#Table(name = "father")
public class Father implements Serializable{
...
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
...
#OneToOne(targetEntity = FotoFather.class, fetch = FetchType.EAGER)
#JoinColumn(name = "fotoFather", referencedColumnName = "id")
#Cascade(CascadeType.ALL)
private FotoFather fotoFather;
}
FotoFather.class
#Entity
#Table(name = "foto_father")
public class FotoFather.class{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
...
#Column(name = "foto_natural_url")
private String fotoNaturalUrl;
...
}
If you simply need the complete URL for some application-specific purpose, I would likely err on the side of not trying to store the URL with the ID at all and instead rely on a transient method.
public class FotoFather {
#Transient
public String getNaturalUrl() {
if(fotoNaturalUrl != null && fotoNaturalUrl.trim().length > 0) {
return String.format("%s?id=%d", fotoNaturalUrl, id);
}
return "";
}
}
In fact, decomposing your URLs even more into their minimalist variable components and only storing those in separate columns can go along way in technical debt, particularly if the URL changes. This way the base URL could be application-configurable and the variable aspects that control the final URL endpoint are all you store.
But if you must know the ID ahead of time (or as in a recent case of mine, keep identifiers sequential without loosing a single value), you need to approach this where FotoFather identifiers are generated prior to persisting the entity, thus they are not #GeneratedValues.
In order to avoid issues with collisions at insertion, we have a sequence service class that exposes support for fetching the next sequence value by name. The sequence table row is locked at read and updated at commit time. This prevents multiple sessions from concurrency issues with the same sequence, prevents gaps in the range and allows for knowing identifiers ahead of time.
#Transactional
public void save(Father father) {
Assert.isNotNull(father, "Father cannot be null.");
Assert.isNotNull(father.getFotoFather(), "FotoFather cannot be null.");
if(father.getFotoFather().getId() == null) {
// joins existing transaction or errors if one doesn't exist
// when sequenceService is invoked.
Long id = sequenceService.getNextSequence("FOTOFATHER");
// updates the fotofather's id
father.getFotoFather().setId(id);
}
// save.
fatherRepository.save(father);
}
I think you can do be registering an #PostPersist callback on your Father class. As the JPA spec notes:
The PostPersist and PostRemove callback methods are invoked for an
entity after the entity has been made persistent or removed. These
callbacks will also be invoked on all entities to which these
operations are cascaded. The PostPersist and PostRemove methods will
be invoked after the database insert and delete operations
respectively. These database operations may occur directly after the
persist, merge, or remove operations have been invoked or they may
occur directly after a flush operation has occurred (which may be at
the end of the transaction). Generated primary key values are
available in the PostPersist method.
So, the callback should be called immediately after the Father instance is written to the database and before the FotoFather instance is written.
public class Father(){
#PostPersist
public void updateFotoFather(){
fotofather.setNaturalUrl("/xyz/" + id);
}
}
In Java, I have access to value of Transient fields of the class. However, I do not access to the same fields on JSP. How can I make them available to JSP?
I am retrieving the values using Hibernate, I reckon a solution to this would be to Transformers.aliasToBean option but is there any other solution to it?
Is there anyway to get rid of transient annotation but have the same mapping in Hibernate? In that case, the problem will be solved.
#AssociationOverrides({
#AssociationOverride(name = "tta.names", joinColumns = #JoinColumn(name = "id"))})
public class Names implements java.io.Serializable {
private static final long serialVersionUID = -30956546435023625398L;
#EmbeddedId
private TableToAssociate tta = new TableToAssociate();
#Transient
public String getid() {
return tta.getMyIds().getId();
}
public void setid(String id) {
this.tta.getMyIds().setId(id);
}
In Java, I can access them using following code
System.out.println(mystudents.getNames().iterator().next().getId());
In JSP, I do not have access to them!
<c:forEach var="nm"items="${mystudents.names}">
${nm.id}
</c:forEach>
If I put another field of names that is not transient, JSP successfully show the value of that item.
Try renaming the methods to match the JavaBean specification.
Instead of:
#Transient
public String getid() {
return tta.getMyIds().getId();
}
public void setid(String id) {
this.tta.getMyIds().setId(id);
}
you should have:
#Transient
public String getId() {
return tta.getMyIds().getId();
}
public void setId(String id) {
this.tta.getMyIds().setId(id);
}
Get rid of #Transient on your entity. Based on your embedded id, you've chosen field annotations. You should be able to have a getter that Hibernate won't try to persist without explicitly marking it as such. And change the getter/setter to use correct JavaBean syntax. getId instead of getid.
It is happening because transient keyword stops the field to be serialized. To pass an object to JSP, it must be serialized. This field will not be there in your serialized object and hence, it is not available to JSP.
For solving your problem, you should see James' comment to use #Transient annotation. It is supplied by JPA and it should allow you to NOT save your value in DB, but serialize it so your JSP can use it.
What you are asking is not possible.. As smarter then me said before.
#Transient is just saying not to serialize/deserialize. Hibernate doesn't serialize this and the same is what jsp bean.
You can do several things:
I think the best thing here is to wrap field in getter and setter in the bean.. This way you will set the property on the bean and in case of only one field every other solution will be overhead.
public class Bean() {
private Names name;
private String id;
public Bean() {
//few action to load name
this.id = name.getid();
}
public String getId() {
return this.id;
}
Create different entity for ui(Bean) and DB(Hibernate). In many cases ui layer and entities are not the same and sometime should not be coupled
You can use inheritance and create diffrent implemenation in the hibernate entity and on ui layer(Jsp bean)
If you ask me i think the best is to do what on section 1. If this happen more oftain you should consider decouple your ui layer and business entity.
We use annotations for mapping the entity class with the database table by simply specifying #Entity and more like #Id, table joins and many things. I do not know how these entity variables are getting mapped with database table. Can anyone give a short description for understanding.
Thanks :)
Well the idea is to translate your objects and their connections with other objects into a relational database. These two ways of representing data (objects defined by classes and in tables in a database) are not directly compatible and that is where a so called Object Relational Mapper framework comes into play.
So a class like
class MyObject
{
private String name;
private int age;
private String password;
// Getters and setters
}
Will translate into a database table containing a column name which is of type varchar, age of type int and password of type varchar.
Annotations in Java simply add additional information (so called meta data) to your class definitions, which can be read by any other class (e.g. JavaDoc) and in the case of the Java Persistence API will be used by an ORM framework like Hibernate to read additional information you need to translate your object into the database (your database table needs a primary id and some information - like what type of a relation an object has to another - can't be automatically determined by just looking at your class definition).
Annotations are very well explained here:
http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/
annotations are just metadata on a class, nothing magical. You can write your own annotations. Those annotations are given retention policies of runtime (which means you have access to that metadata at runtime). When you call persist etc the persistence provider iterates through the fields (java.lang.reflect.Field) in your class and checks what annotations are present to build up your SQL statement. Try writing your own annotation and doing something with it. It won't seem very magical after that.
in your case annotation working means mapping with tablename with entity class is look like as ....
#Entity
#Table(name = "CompanyUser")
public class CompanyUserCAB implements java.io.Serializable
{
private long companyUserID;
private int companyID;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "companyUserID")
public long getCompanyUserID()
{
return this.companyUserID;
}
public void setCompanyUserID(long companyUserID)
{
this.companyUserID = companyUserID;
}
#Column(name = "companyID")
public int getCompanyID()
{
return this.companyID;
}
public void setCompanyID(int companyID)
{
this.companyID = companyID;
}
}