hibernate question ?

M

mike

I have an entity like
@Entity
public class Address {
@Id
private int id;
private String street;
private String city;
private String state;
private String zip;
private Set<Address> addressSet = new HashSet<Address>();

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getStreet() {
return street;
}

public void setStreet(String address) {
this.street = address;
}

public String getCity() {
return city;
}

public void setCity(String city) {
this.city = city;
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

public String getZip() {
return zip;
}

public void setZip(String zip) {
this.zip = zip;
}
public String toString() {
return "Address id: " + getId() +
", street: " + getStreet() +
", city: " + getCity() +
", state: " + getState() +
", zip: " + getZip();
}

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy =
"address")
public Set<Address> getAddressSet() {
return this.addressSet;
}

public void setAuthDevices(Set<Address> address) {
this.addressSet = address;
}



}

and entity :

@Entity
public class Student {
@Id
private int id;
private String name;

@ManyToOne(cascade=CascadeType.PERSIST)
Address address;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Address getAddress() {
return address;
}

public void setAddress(Address address) {
this.address = address;
}

public String toString() {
return "Student id: " + getId() + " name: " + getName() +
" with " + getAddress();
}
}


if i do this :

Student emp = new Student();
emp.setId(1);
emp.setName("name");
Address addr = new Address();
addr.setId(1);
addr.setStreet("street");
addr.setCity("city");
addr.setState("state");
emp.setAddress(addr);
addr.getAddressSet().add(emp);
em.persist(emp);

the cascade attribute works and two insert are generated(cascade persist
works, but when
I do something like this :

Student emp = em.find(Student.class, 1L);
emp.setName("name");
Address addr = em.find(Adress.class, 1L);
addr.setStreet("streetOne");
emp.setAddress(addr);
em.persist(emp);

two sql updates are generates and address and student is updates, but
there is NO cascade = MERGE on Student entity... how is this POSSIBLE ?
 
L

Lew

I have an entity like
@Entity
public class Address {
@Id
private int id;
private String street;
private String city;
private String state;
private String zip;
private Set<Address> addressSet = new HashSet<Address>();

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

...

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY,
mappedBy = "address")
public Set<Address> getAddressSet() {
return this.addressSet;
}

public void setAuthDevices(Set<Address> address) {
this.addressSet = address;
}
}

and entity :

@Entity
public class Student {
@Id
private int id;
private String name;

@ManyToOne(cascade=CascadeType.PERSIST)
Address address;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

...
}


if i [sic] do this :

Student emp = new Student();
emp.setId(1);
emp.setName("name");
Address addr = new Address();
addr.setId(1);
addr.setStreet("street");
addr.setCity("city");
addr.setState("state");
emp.setAddress(addr);
addr.getAddressSet().add(emp);
em.persist(emp);

the cascade attribute works and two insert are generated(cascade persist
works, but whenI do something like this :

Student emp = em.find(Student.class, 1L);
emp.setName("name");
Address addr = em.find(Adress.class, 1L);
addr.setStreet("streetOne");
emp.setAddress(addr);
em.persist(emp);

two sql updates are generates and address and student is updates, but
there is NO cascade = MERGE on Student entity... how is this POSSIBLE ?

There's no need to shout so loudly.

I suspect but do not know that it has to do with mixing field and method
annotations in the same class. Don't do that anyway.

It might be coincidence that the cascade specified in the class where you did
that is the one that didn't work.

You probably don't need to initialize 'Address#addressSet' explicitly. I'm
puzzled why people do that in entity classes. What does it provide?
 
F

Frank Langelage

I have an entity like
@Entity
public class Address {
@Id
private int id;
private String street;
private String city;
private String state;
private String zip;
private Set<Address> addressSet = new HashSet<Address>();

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getStreet() {
return street;
}

public void setStreet(String address) {
this.street = address;
}

public String getCity() {
return city;
}

public void setCity(String city) {
this.city = city;
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

public String getZip() {
return zip;
}

public void setZip(String zip) {
this.zip = zip;
}
public String toString() {
return "Address id: " + getId() +
", street: " + getStreet() +
", city: " + getCity() +
", state: " + getState() +
", zip: " + getZip();
}

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy =
"address")
public Set<Address> getAddressSet() {
return this.addressSet;
}

public void setAuthDevices(Set<Address> address) {
this.addressSet = address;
}



}

and entity :

@Entity
public class Student {
@Id
private int id;
private String name;

@ManyToOne(cascade=CascadeType.PERSIST)
Address address;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Address getAddress() {
return address;
}

public void setAddress(Address address) {
this.address = address;
}

public String toString() {
return "Student id: " + getId() + " name: " + getName() +
" with " + getAddress();
}
}


if i do this :

Student emp = new Student();
emp.setId(1);
emp.setName("name");
Address addr = new Address();
addr.setId(1);
addr.setStreet("street");
addr.setCity("city");
addr.setState("state");
emp.setAddress(addr);
addr.getAddressSet().add(emp);
em.persist(emp);

the cascade attribute works and two insert are generated(cascade persist
works, but when
I do something like this :

Student emp = em.find(Student.class, 1L);
emp.setName("name");
Address addr = em.find(Adress.class, 1L);
addr.setStreet("streetOne");
emp.setAddress(addr);
em.persist(emp);

two sql updates are generates and address and student is updates, but
there is NO cascade = MERGE on Student entity... how is this POSSIBLE ?

You're calling EntityManager#persist() here again, not
EntityManager#merge().
So from my understanding the behavior is correct.
Try using the merge() operation for the update and see if anything changes.
 
T

Tom Anderson

I have an entity like
@Entity
public class Address {
@Id
private int id;
private String street;
private String city;
private String state;
private String zip;
private Set<Address> addressSet = new HashSet<Address>();

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

...

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY,
mappedBy = "address")
public Set<Address> getAddressSet() {
return this.addressSet;
}

public void setAuthDevices(Set<Address> address) {
this.addressSet = address;
}
}

and entity :

@Entity
public class Student {
@Id
private int id;
private String name;

@ManyToOne(cascade=CascadeType.PERSIST)
Address address;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

...
}


if i [sic] do this :

Student emp = new Student();
emp.setId(1);
emp.setName("name");
Address addr = new Address();
addr.setId(1);
addr.setStreet("street");
addr.setCity("city");
addr.setState("state");
emp.setAddress(addr);
addr.getAddressSet().add(emp);
em.persist(emp);

the cascade attribute works and two insert are generated(cascade persist
works, but whenI do something like this :

Student emp = em.find(Student.class, 1L);
emp.setName("name");
Address addr = em.find(Adress.class, 1L);
addr.setStreet("streetOne");
emp.setAddress(addr);
em.persist(emp);

two sql updates are generates and address and student is updates, but
there is NO cascade = MERGE on Student entity... how is this POSSIBLE ?

There's no need to shout so loudly.

I suspect but do not know that it has to do with mixing field and method
annotations in the same class. Don't do that anyway.

It might be coincidence that the cascade specified in the class where you did
that is the one that didn't work.

You probably don't need to initialize 'Address#addressSet' explicitly. I'm
puzzled why people do that in entity classes. What does it provide?

The ability to say new Address().getAddressSet().add(something) without
getting a NullPointerException. Didn't we talk about this before?

I'm more concerned about the setter for the ID field myself.

Well, and the fact that the OP has put the address set in the address
rather than in the student, that the setter for it is called
setAuthDevices, that he calls his Student variable 'emp', and various
other small signs that he has no idea what he's doing.

tom
 
M

mike

You're calling EntityManager#persist() here again, not
EntityManager#merge().
So from my understanding the behavior is correct.
Try using the merge() operation for the update and see if anything changes.

Sorry, i wrote wrong... in the second piece of code I meant
em.merge(emp). That is what is confusing... putting fetch =
fetchType.MERGE or removing it, has the same effect, 2 update
sql are generated. why ?
 
T

Tom Anderson

That would only be guaranteed if the setter rejects or substitutes
'null' values, which the OP did not show.

I don't follow.

My understanding (caveat: i am not very familiar with JPA, and am hung
over) is that we have a situation analogous to:

public class Person {
@Id
private int id;
@OneToMany
private Set<Animal> pets;
// getters and setters
}

If you got one of these from the database, you could happily say:

person.getPets().add(someNewPet);

and everything would be okay. But if you created a new Person out of thin
air with new, it would blow up, because at that point, pets is null. Hence
the pattern of putting in an initializer which creates an empty
collection. It doesn't interfere with the case where you're getting the
object from the database, because it will immediately be overwritten with
one of Hibernate's persistent collections.

You could save the wasted objects by doing lazy initialisation of the
field, so one only gets allocated if it's needed, but i doubt the saving
would be significant in any but the most extreme situations.
In fact, they don't show a setter for that field at all, so one wonders
how any useful information gets into it.

Good point. There is a setter, but under the wrong name, where i assume
Hibernate won't find it. If the persistence annotation was on the field
instead of the getter, things would work, but it ain't.
I remain dubious about the value of preventing null in the retrieved
value. None of the other values are thus guarded. With any
non-primitive entity attribute, and that's nearly all entity attributes,
you have to check for possible 'null' values if the corresponding data
store might contain NULL.

If it's a collection, i don't think it can be null (unless you're storing
it as a serialized object rather than a database-mapped collection), since
there is no actual column holding its value - rather, there are
backreference columns in contained objects. Those could be null, but that
just means those objects aren't in any collection. If no object is
contained in the collection, won't Hibernate make it an empty collection,
rather than null?

tom
 
L

Lew

(Just because I elided tom's other comments doesn't mean they aren't
worthwhile, but these last summarize.)

Tom said:
If it's a collection, i don't think it can be null (unless you're
storing it as a serialized object rather than a database-mapped
collection), since there is no actual column holding its value - rather,
there are backreference columns in contained objects. Those could be
null, but that just means those objects aren't in any collection. If no
object is contained in the collection, won't Hibernate make it an empty
collection, rather than null?

Your comments addressed my puzzlement perfectly. You've given me some things
to test. Thanks, tom.
 
A

Arved Sandstrom

mike said:
On 28.8.2010. 11:38, Frank Langelage wrote: [ SNIP ]
You're calling EntityManager#persist() here again, not
EntityManager#merge().
So from my understanding the behavior is correct.
Try using the merge() operation for the update and see if anything
changes.

Sorry, i wrote wrong... in the second piece of code I meant
em.merge(emp). That is what is confusing... putting fetch =
fetchType.MERGE or removing it, has the same effect, 2 update
sql are generated. why ?

Why do you think you need to do a merge? The whole point of find() is to
locate an entity by its primary key and bring it into the persistence
context.Since it's in a PC it's managed and there is no need to merge.

Rule of thumb: once an entity is managed then changes to its state will
result in an update; you don't have to call persist() or merge() to save
altered state. I've seen a lot of JPA newcomers erroneously assume that they
have to do this. Just let the commit at the explicit or implicit end of the
transaction (depends on whether you have application or container-managed
EM's, whether it's JTA or resource local etc) take care of business for you.

Since changes to state of managed entities are what cause updates, and you
did change the state of both entities, that's why 2 updates. This has
nothing to do with your cascades at all.

AHS

Before a man speaks it is always safe to assume that he is a fool.
After he speaks, it is seldom necessary to assume it. -- H.L. Mencken
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,981
Messages
2,570,188
Members
46,732
Latest member
ArronPalin

Latest Threads

Top