When I use #Transactional annotation many-to-one relations come null - java

When I use #Transactional, after I save objects, hibernate get function returns object which join fields are null.
This is my business layer. #Transactional annotation is there.
#Override
#Transactional(propagation = Propagation.REQUIRED)
public KonKonfigCoverView addWithNewObj(KonKonfigCoverView objView, Long kurumId)
throws MosBaseException {
EnvanterKitTanimView kitView = null;
if (objView.getKit() != null){
kitView = objView.getKit();
if ("0001".equals(kitView.getEnvanterKitTip().getKod())){ //ATS
kitView = atsKitBO.addObj(kitView, kurumId);
}
else if("0002".equals(kitView.getEnvanterKitTip().getKod())){ //OGSHGS
kitView = ogsHgsBO.addObj(kitView, kurumId);
}
else if("0004".equals(kitView.getEnvanterKitTip().getKod())){ //TTS
kitView = akaryakitKitBO.addObj(kitView, kurumId);
}
objView.getKonKonfig().setKit(kitView);
objView.getKonKonfigDegisiklikDetay().setKit(kitView);
objView.setKit(kitView);
}
KonKonfigCoverView view = this.addObj(objView, kurumId);
if (objView.getKit() != null){
view.setKit(kitView);
}
return view;
}
This is the this.addObj function:
#Override
public KonKonfigView addObj(KonKonfigView view, Long kurumId) throws MosBaseException{
KonKonfig addedObj = konKonfigDAO.addObj(beforeAddObj, ilkTakilmaOdo, sonTakilmaOdo);
addedObj= konKonfigDAO.getObj(addedObj.getId());
return (KonKonfigView) BaseHelper.gulmekYok(KonKonfig.class, KonKonfigView.class, addedObj, true, EnViewTip.FULL_DESCRIPTION, null);
}
This add operation works well, however getObj function returns object that many-to-one relation fields are null.
One of my fields which comes null is that:
#ManyToOne(optional=true)
#JoinColumn(name="CAR_ID", insertable=false, updatable=false)
private Car car;

Unless you are saving the car object before you save the enclosing object, then the field will definitely be null. Hibernate sets the cascade type for a #ManyToOne mapping to none.
This means that operations that were done on the enclosing object will not be done to the objects mapped inside unless you specify a specific cascade type.
You can do the following to solve this problem:
Option 1: #ManyToOne(cascade = CascadeType.PERSIST)
Option 2:
#ManyToOne
#Cascade(CascadeType.PERSIST)
Checkout the different CascadeTypes so you can choose the right one for what you need.

Related

LazyInitializationException on getId() of a #ManyToOne reference

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();
}

hibernate findbyid causes update?

I faced with a very strange behavior in my web app with spring 3 and hibernate-core 3.5.1-Final.
For simplicity i provide my code..
if(ripid!=null){ //Parameter
Appuntamento apDaRip = appuntamentoService.findById(ripid);
if(apDaRip.getIdpadre()!=null){
apDaRip.setNota("RIPROGRAMMATO n."+ripid.toString()+"\n"+apDaRip.getNota());
apDaRip.setIdpadre(apDaRip.getIdpadre());
}else{
apDaRip.setNota("RIPROGRAMMATO n."+ripid.toString()+"\n"+apDaRip.getNota());
apDaRip.setIdpadre(ripid);
}
try{
apDaRip.setOrarioinizio(null);
apDaRip.setDurata(null);
//apDaRip.setIdappuntamento(null);
}catch(Exception e){e.printStackTrace();}
map.put("appuntamento", apDaRip);
}
di = datiintranetService.findById(DatiintranetService.PASS_X_INTERVENTI);
map.put("passinterventi", di.getBoolean());
The idea behind is to use some data of an object "Appuntamento" for produce a new one.
So i'm going to change some value and before send the object to my view (jsp) i fetch other data by calling findbyid. This cause an update to the Appuntamento object... Off course i don't want this behavior. Someone can have an explanation of this?
Edit-1
Here's the Dao
#Transactional
public class DatiintranetService {
private DatiintranetDAO datiintranetDAO;
public void setDatiintranetDAO(DatiintranetDAO datiintranetDAO) {
this.datiintranetDAO = datiintranetDAO;
}
public DatiintranetDAO getDatiintranetDAO() {
return datiintranetDAO;
}
public Datiintranet findById(Integer id) {
return datiintranetDAO.findById(id);
}
}
and For Appuntamento class I provide to you a snapshot
#Entity
#Table(name = "appuntamento", schema = "public")
public class Appuntamento implements java.io.Serializable {
#Id
#SequenceGenerator(name="appuntamentoID", sequenceName="appuntamento_idappuntamento_seq",allocationSize =1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="appuntamentoID")
#Column(name = "idappuntamento", unique = true, nullable = false)
public Integer getIdappuntamento() {
return this.idappuntamento;
}
}
Edit-2
IF i move thoese two row above the if statement no update occur.
di = datiintranetService.findById(DatiintranetService.PASS_X_INTERVENTI);
map.put("passinterventi", di.getBoolean());
If you query for an entity and change the entity, the default behavior is to persist those changes via an update to the database. This is usually what you want to happen, but obviously not in all cases.
If you want to avoid the update, you need to detach the entity by calling session.evict(apDaRip) where session is a reference to the hibernate session (see Session.evict()). You probably want to evict the entity right after you get it (immediately following the call to findById).

Delete a record on an OneToMany association with Hibernate

I am working on an application using Hibernate and I want to delete some records in the database. The relevant Entities are:
#Entity
public class Product {
private String serialNumber;
private Set<Part> parts = new HashSet<Part>();
#Id
public String getSerialNumber() { return serialNumber; }
void setSerialNumber(String sn) { serialNumber = sn; }
#OneToMany
public Set<Part> getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
...
}
#Entity
public class Part implements Serializable {
#Id
#GeneratedValue
private Long part_id;
private String userCode = "";
//getters and setters
....
}
I have let Eclipse implement equals and hashCode in Entity Part based on part_id and userCode. There is also an Entity Factory from which 'begin' all the associations to the other Entities. Therefore, in order to save all the changes it only necessary to execute the comand:
session.update(factory);
All the changes are saved successfully except from the delete from parts. I do:
products.getParts.remove(part);
The issues comig out are:
1) In some cases is part from the Set not removed although the comparison to a part in the Set with equals true returns (the part is in Set according to equals but it is not removed)
2) Even if the remove in the Set succeeds, the record in the database is not deleted.
Based on the above ascertainments what is the best way to remove the records in this case using not loads of queries?
You need to explicitly remove the child:
session.delete(part);
From Hibernate Docs:
The following code:
Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
c.setParent(null);
session.flush();
will not remove c from the database. In this case, it will only remove
the link to p and cause a NOT NULL constraint violation. You need to
explicitly delete() the Child.
Parent p = (Parent) session.load(Parent.class, pid);
Child c = (Child) p.getChildren().iterator().next();
p.getChildren().remove(c);
session.delete(c);
session.flush();
When using hibernate to map relationships you must be aware of two main concerns:
Which is the owner of the relationship? The owner is the side of the relation whose changes will be persisted in database. In your case the owner is the Part object.
Is a true parent/child relationship or simply a composition relationship? In your case I think the answer is composition
If you want to manage the relation using the set, you have two options:
use #ElementCollection instead of #OnetoMany
change ownership. Something like this:
#OneToMany
#JoinColumn(name="part_id")
public Set<Part> getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
However, the second option is not recommended here. See section 2.2.5.3.1.2.

JPA OneToMany - Collection is null

I'm trying to set up a bidirectional relationship using JPA. I understand that it's the responsability of the application to maintain both sides of the relationship.
For example, a Library has multiple Books. In the Library-entity I have:
#Entity
public class Library {
..
#OneToMany(mappedBy = "library", cascade = CascadeType.ALL)
private Collection<Book> books;
public void addBook(Book b) {
this.books.add(b);
if(b.getLibrary() != this)
b.setLibrary(this);
}
..
}
The Book-entity is:
#Entity
public class Book {
..
#ManyToOne
#JoinColumn(name = "LibraryId")
private Library library;
public void setLibrary(Library l) {
this.library = l;
if(!this.library.getBooks().contains(this))
this.library.getBooks().add(this);
}
..
}
Unfortunately, the collection at the OneToMany-side is null. So for example a call to setLibrary() fails because this.library.getBooks().contains(this) results in a NullPointerException.
Is this normal behavior? Should I instantiate the collection myself (which seems a bit strange), or are there other solutions?
Entities are Java objects. The basic rules of Java aren't changed just because there is an #Entity annotation on the class.
So, if you instantiate an object and its constructor doesn't initialize one of the fields, this field is initialized to null.
Yes, it's your responsibility to make sure that the constructor initializes the collection, or that all the methods deal with the nullability of the field.
If you get an instance of this entity from the database (using em.find(), a query, or by navigating through associations of attached entities), the collection will never be null, because JPA will always initialize the collection.
It seems that books type of Collection in Library class is not initilized. It is null;
So when class addBook method to add a book object to collection. It cause NullPointerException.
#OneToMany(mappedBy = "library", cascade = CascadeType.ALL)
private Collection<Book> books;
public void addBook(Book b) {
this.books.add(b);
if(b.getLibrary() != this)
b.setLibrary(this);
}
Initilize it and have a try.
Change
private Collection<Book> books;
To
private Collection<Book> books = new ArrayList<Book>();
Try to set the fetch type association property to eager on the OneToMany side. Indeed, you may leave this part (this.library.getBooks().add(this)) to be written within a session:
Library l = new Library();
Book b = new Book();
b.setLibrary(l);
l.getBooks().add(b);

JPA 2.0 insufficient privileges when performing insert from transaction

After much searching and trials, I am stuck... I have two classes, one is ExpectedSecurityReturn and the other is ForecastReturnType. ForecastReturnType is a member of ExpectedSecurityReturn but should not be inserted when persisting data. I keep getting an "insufficient privileges" but I know that the user does have the delete/insert privileges to the table expected_security_return since I tested with JDBC and JPA delete works fine. Therefore, I think that it has to do with my classes.
#Table(name = "EXPECTED_SECURITY_RETURNS")
#Entity
#IdClass(ExpectedSecurityReturn.ExpectedSecurityReturnPK.class)
public class ExpectedSecurityReturn {
#Id
#Column(name = "REP_SEC_ID")
private Integer repSecId;
#Id
#Column(name = "AS_OF_DATE")
private Date date;
#Id
#ManyToOne(optional = false)
#JoinColumn(name = "RETURN_TYPE_ID", referencedColumnName = "RETURN_TYPE_ID", insertable=false)
private ForecastReturnType returnType;
#Column(name="CURR_TOUSD_RET") // local currency to usd
private Double currencyToUsdReturn;
}
The primary key class, which includes ForecastReturnType:
// ------------------------------
// PK
// ------------------------------
public static class ExpectedSecurityReturnPK implements Serializable {
private static final long serialVersionUID = 1325372032981567439L;
public ExpectedSecurityReturnPK() {
}
public ExpectedSecurityReturnPK(final Integer repSecId,
final Date asOfDate, ForecastReturnType returnType) {
if (repSecId == null)
throw new IllegalArgumentException("null rep sec id");
if (asOfDate == null)
throw new IllegalArgumentException("null asOfDate");
if (returnType == null)
throw new IllegalArgumentException("null returnType");
this.repSecId = repSecId;
this.date = new Date(asOfDate.getTime());
}
#Override
public boolean equals(final Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
final ExpectedSecurityReturnPK that = (ExpectedSecurityReturnPK) o;
if (repSecId != that.repSecId)
return false;
if (!date.equals(that.date))
return false;
if (!returnType.equals(that.returnType))
return false;
return true;
}
#Override
public int hashCode() {
int result = repSecId;
result = 31 * result + date.hashCode();
result = 31 * result + returnType.getForecastTypeId();
return result;
}
private int repSecId;
private Date date;
private ForecastReturnType returnType;
}
and ForecastReturnType:
#Table(name="EXPECTED_SEC_RET_TYPE_DECODE")
#Entity
public class ForecastReturnType {
#Id
#Column(name="RETURN_TYPE_ID")
private int forecastTypeId;
#Column(name="SHORT_NAME")
private String shortName;
#Column(name="LONG_NAME")
private String longName;
#OneToMany(fetch=FetchType.LAZY, mappedBy="returnType")
Collection<ExpectedSecurityReturn> expectedSecurityReturns;
}
Could anyone help me figure out what I am doing wrong? I tried many things without success... I think that the culprit is ExpectedSecurityReturn.returnType since I know that the user does not have privileges.
Basically, I need to insert/persist ExpectedSecurityReturn instances.
Well, there's a couple of things.
I would heavily not recommend even trying to do this. You can waste away your life figuring out JPA annotations and weird issues like this that never quite seem to work right. You'll also find that different JPA providers will behave slightly differently when it comes to more complex structures like this, and it goes doubly for inheritance.
You're really much better off creating a unique key on EXPECTED_SECURITY_RETURNS, and just living with it, it will make your Java life much much easier.
If you have to do something like this, I'm not surprised that JPA is balking at having a primary key component be another entity object. Whilst this in of course quite possible in the RDBMS, it's seemingly little things like this that will trip up JPA.
I would also check the query logs that your JPA impl will put out (it's configurable fairly easily in the persistence definition for most JPA providers, certainly Ecpliselink and Hibernate). I'd be willing to bet it's trying to run an update on EXPECTED_SEC_RET_TYPE_DECODE, and if not, it might be trying to obtain a lock (table, row or other depending on your DBMS). If the user doesn't have permission to either execute a lock or an update on that table, depending on the exact implementation, the query could fail with a permissions problem.
It is reasonable for JPA to want to hold a lock on that table because there is a chance that during the transaction, the entry that is being referenced in EXPECTED_SEC_RET_TYPE_DECODE may get changed, so it must ensure that it doesn't whilst updating/inserting on the other table. Last I checked, there is no way to tell JPA that this table is essentially static. If you're using Hibernate, you might try the #ReadOnly annotation, but in the past, not much I've tried can get around things like this.
If you do find a better solution, feel free to post it so that the rest of us can learn!!
I agree with PlexQ that derived identities and composite keys are pretty complicated parts of JPA.
However, JPA 2.0 specification contains a good set of examples to illustrate these topics, and these examples mostly work across different JPA implementations.
For your case specification suggests you to put into #IdClass a field with name of #ManyToOne field and type of #Id field of referenced entity:
#Entity
public class Employee {
#Id long empId;
String empName;
...
}
public class DependentId {
String name; // matches name of #Id attribute
long emp; // matches name of #Id attribute and type of Employee PK
}
#Entity
#IdClass(DependentId.class)
public class Dependent {
#Id String name;
// id attribute mapped by join column default
#Id #ManyToOne Employee emp;
...
}
See also:
JSR 317: JavaTM Persistence 2.0
After a lot of trial and error, I finally figured out that the error was legitimate and I did not indeed have sufficient (ie insert) privileges, only delete!!

Categories

Resources