Reputation: 1703
I'm trying to update entity which contains fields of type of another class.
so this is my entity:
@Entity
public class Owner {
@Id
@GeneratedValue
private int id;
@Column(name = "first_name")
@NotNull(message="{NotNull}")
@Size(min=2,max=15,message="{Size}")
private String firstName;
@NotNull(message="{NotNull}")
@Size(min=2,max=15,message="{Size}")
@Column(name = "last_name")
private String lastName;
@Valid
@OneToOne(cascade = CascadeType.ALL)
private Phone phone;
@Valid
@OneToOne(cascade = CascadeType.ALL)
private Pet pet;
from this view:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1"></meta>
<title>Owner details</title>
</head>
<body>
<div id="owner">
<form th:action="@{|/ownerList/${owner.id}.do|}"
th:object="${owner}" method="post">
<table>
<tr>
<td>Id:</td>
<td><input type="text" th:field="*{id}" /></td>
<td th:if="${#fields.hasErrors('id')}" th:errors="*{id}">fieldError</td>
</tr>
<tr>
<td>First name</td>
<td><input type="text" th:field="*{firstName}" /></td>
<td th:if="${#fields.hasErrors('firstName')}"
th:errors="*{firstName}">fieldError</td>
</tr>
<tr>
<td>Last name</td>
<td><input type="text" th:field="*{lastName}" /></td>
<td th:if="${#fields.hasErrors('lastName')}"
th:errors="*{lastName}">fieldError</td>
</tr>
<tr>
<td>Phone</td>
<td><input type="text" th:field="*{phone.number}" /></td>
<td th:if="${#fields.hasErrors('phone.number')}"
th:errors="*{phones[0].number}">fieldError</td>
</tr>
<tr>
<td>Pet</td>
<td><input type="text" th:field="*{pet.petName}" /></td>
<td th:if="${#fields.hasErrors('pet.petName')}"
th:errors="*{pet.petName}">fieldError</td>
</tr>
<tr>
<td><input type="submit" value="update" name="action" /></td>
<td><input type="submit" value="delete" name="action" /></td>
</tr>
</table>
</form>
<a href="/ownerList">Back</a>
</div>
</body>
</html>
I'm using this controller:
@RequestMapping(value = "/ownerList/{id}.do")
public String ownerDetailsDo(@ModelAttribute(value = "owner") Owner owner, BindingResult result,
@RequestParam(value = "action") String action, Model model) {
switch (action) {
case "update":
ObjectBinder.bind(owner);
ownerService.update(owner);
return "ownerDetail";
case "delete":
ownerService.remove(owner.getId());
model.addAttribute("ownerList", ownerService.getAll());
return "ownerList";
}
model.addAttribute("owner", owner);
return "ownerDetail";
}
So I'm trying to update object of Owner, but inside of database after .merge I can find new entity of , for example Phone, with new Id.
so to make clear for example I have: Owner: first name:XYZ last name: BBB pet: BOB phone: 1234
when I try to update phone, lets say to "2222", then in DB I can find two records one is "1234" second is "2222", and I want to have "2222" replaced the old one "1234".
Upvotes: 1
Views: 85
Reputation: 1697
If you want to delete the phone when is not referenced by any Owner then you need to add @OrphanRemoval.
Orphan Removal in Relationships When a target entity in one-to-one or one-to-many relationship is removed from the relationship, it is often desirable to cascade the remove operation to the target entity. Such target entities are considered “orphans,” and the orphanRemoval attribute can be used to specify that orphaned entities should be removed. For example, if an order has many line items and one of them is removed from the order, the removed line item is considered an orphan. If orphanRemoval is set to true, the line item entity will be deleted when the line item is removed from the order.
The orphanRemoval attribute in @OneToMany and @oneToOne takes a Boolean value and is by default false.
The following example will cascade the remove operation to the orphaned customer entity when it is removed from the relationship:
@OneToMany(mappedBy="customer", orphanRemoval="true") public List getOrders() { ... }
@Entity
public class Owner {
@Id
@GeneratedValue
private int id;
@Column(name = "first_name")
@NotNull(message="{NotNull}")
@Size(min=2,max=15,message="{Size}")
private String firstName;
@NotNull(message="{NotNull}")
@Size(min=2,max=15,message="{Size}")
@Column(name = "last_name")
private String lastName;
@Valid
@OneToOne(cascade = CascadeType.ALL, orphanRemoval="true")
private Phone phone;
@Valid
@OneToOne(cascade = CascadeType.ALL, orphanRemoval="true")
private Pet pet;
And if you do this:
owner.set(new Phone(2222));
entityManager.merge(owner));
// update the owner phone
owner.set(new Phone(77777));
//the phone(2222) will be deleted
entityManager.merge(owner));
Upvotes: 1
Reputation: 7273
This is happening because you're using phone number as @Id
for your Phone
entity. And you cannot change the ID of an entity: you can either create a new one with a different ID, or findById(number)
the existing one and update the value of the other fields (but not the @Id
field, since that would again create a new entity).
If the phone number ("1234" or "2222") is all you need, then there's no point in having a Phone
class to begin with. You can just use a String
for that (if you want to be able to use +XX XXX ... format, or validate phone number length) or even an Integer
.
If you do need the Phone
class because it has other fields (like Phone.totalCallsMade
or the like), then your program works as expected: different number, different entity.
Upvotes: 0