Reputation:
I'm using MySQL, Hibernate and Spring MVC in my CRUD application. I have two entities in one-to-one unidirectional relationship. When I delete User, address is still in database. The user and address are added correctly. What's wrong? I checked all the topics on stack and did not find the reason of this fail... Below is my java code and SQL scripts.
User.java
@Table
@Entity(name = "User")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "address_id")
private Address address;
public User() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", address=" + address + "]";
}
}
Address.java
@Entity
@Table(name = "address")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Column(name = "street")
private String street;
@Column(name = "city")
private String city;
@Column(name = "zip_code")
private String zipCode;
public Address() {
}
public Address(String street, String city, String zipCode) {
this.street = street;
this.city = city;
this.zipCode = zipCode;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
@Override
public String toString() {
return "Address [id=" + id + ", street=" + street + ", city=" + city + ", zipCode=" + zipCode + "]";
}
}
GenericService.java
public interface GenericService<T> {
List<T> getAll();
void save(T t);
User getOne(int id);
void delete(int id);
}
UserService.java
@Service
public class UserService implements GenericService<User> {
@Autowired
private UserDAO userDao;
@Override
@Transactional
public List<User> getAll() {
return userDao.getAll();
}
@Override
@Transactional
public void save(User user) {
userDao.save(user);
}
@Override
@Transactional
public User getOne(int id) {
return userDao.getOne(id);
}
@Override
@Transactional
public void delete(int id) {
userDao.delete(id);
}
}
AbstractDAO.java
public abstract class AbstractDAO<T> {
@Autowired
public SessionFactory sessionFactory;
protected Class<T> entityClass;
public AbstractDAO() {
ParameterizedType pt = (ParameterizedType) getClass().getGenericSuperclass();
entityClass = (Class<T>) pt.getActualTypeArguments()[0];
}
public T getOne(int id) {
Session session = sessionFactory.getCurrentSession();
T t = session.get(entityClass, id);
return t;
}
public List<T> getAll() {
Session session = sessionFactory.getCurrentSession();
TypedQuery<T> query = session.createQuery("from " + entityClass.getSimpleName(), entityClass);
List<T> t = query.getResultList();
return t;
}
public T save(T t) {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(t);
return t;
}
public void delete(int id) {
Session session = sessionFactory.getCurrentSession();
TypedQuery<T> query = session.createQuery("delete from " + entityClass.getSimpleName() + " where id=:id");
query.setParameter("id", id);
query.executeUpdate();
}
}
UserDAO.java
@Repository
public class UserDAO extends AbstractDAO<User>{
@Autowired
private SessionFactory sessionFactory;
public List<User> getAll(){
Session session = sessionFactory.getCurrentSession();
TypedQuery<User> query = session.createQuery("from "+entityClass.getSimpleName(),entityClass);
List<User> userList = query.getResultList();
return userList;
}
public User save(User user) {
Session currentSession = sessionFactory.getCurrentSession();
currentSession.saveOrUpdate(user);
return user;
}
public void delete(int id) {
Session session = sessionFactory.getCurrentSession();
TypedQuery<User> query = session.createQuery("delete from "+ entityClass.getSimpleName()+" where id=:id");
query.setParameter("id",id);
query.executeUpdate();
}
}
UserController.java
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@InitBinder
public void initBinder(WebDataBinder dataBinder) {
StringTrimmerEditor stringTrimmerEdition = new StringTrimmerEditor(true);
dataBinder.registerCustomEditor(String.class, stringTrimmerEdition);
}
@GetMapping("/list")
public String userList(Model theModel) {
List<User> theUsers = userService.getAll();
theModel.addAttribute("users", theUsers);
return "list-users";
}
@GetMapping("/showFormForAdd")
public String showFormForAdd(Model theModel) {
User theUser = new User();
theModel.addAttribute("user", theUser);
return "user-form";
}
@PostMapping("/saveUser")
public String saveUser(@ModelAttribute("user") User theUser, BindingResult bindingResult) {
userService.save(theUser);
return "redirect:/user/list";
}
@GetMapping("/showFormForUpdate")
public String showFormForUpdate(@RequestParam("userId") int theId, Model theModel) {
User user = userService.getOne(theId);
theModel.addAttribute("user", user);
return "user-form";
}
@GetMapping("/delete")
public String deleteUser(@RequestParam("userId") int theId) {
userService.delete(theId);
return "redirect:/user/list";
}
}
list-form.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous">
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
<title>SimpleCRUD</title>
</head>
<body>
<nav class="navbar navbar-dark bg-primary" style="color: white">
<h3>
<i>SimpleCRUD</i>
<h3>
</nav>
<div class="container-fluid text-center">
<div class="row">
<div class="col-md-12">
<br>
<h3>
<i>Lista użytkowników</i>
</h3>
<br>
<table class="table table-sm text-center">
<thead>
<tr>
<th>Imię</th>
<th>Nazwisko</th>
<th>Ulica</th>
<th>Miasto</th>
<th>Kod pocztowy</th>
<th></th>
</tr>
</thead>
<tbody>
<c:forEach var="tempUser" items="${users}">
<c:url var="updateLink" value="/user/showFormForUpdate">
<c:param name="userId" value="${tempUser.id}" />
</c:url>
<c:url var="deleteLink" value="/user/delete">
<c:param name="userId" value="${tempUser.id}" />
</c:url>
<tr>
<td>${tempUser.firstName}</td>
<td>${tempUser.lastName}</td>
<td>${tempUser.address.street}</td>
<td>${tempUser.address.city}</td>
<td>${tempUser.address.zipCode}</td>
<td><a href="${updateLink}">Aktualizuj</a> | <a
href="${deleteLink}"
onclick="if(!(confirm('Usunąć użytkownika z bazy?'))) return false">Usuń</a></td>
</tr>
</c:forEach>
</tbody>
<p><i>Liczba użytkowników: ${fn:length(users)}</p>
</table>
<button type="button" class="btn btn-primary"
onclick="window.location.href='showFormForAdd'; return false;">Dodaj
użytkownika</button>
</div>
</div>
</div>
<footer>
<br>
<hr>
<center>
<i>SimpleCRUD 2018</i>
</center>
</footer>
</body>
</html>
user-form.jsp
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous">
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
<title>SimpleCRUD</title>
</head>
<body>
<nav class="navbar navbar-dark bg-primary" style="color: white">
<h3>
<i>SimpleCRUD</i>
<h3>
</nav>
<div class="container">
<div class="row">
<div class="col-md-4">
<br>
<h3>
<i>Dodaj użytkownika</i>
</h3>
<br>
<form:form action="saveUser" modelAttribute="user" method="POST">
<form:hidden path="id" />
<form:hidden path="address.id" />
<div class="form-group">
<label>Imię: </label>
<form:input class="form-control" path="firstName" />
<form:errors path="firstName"/>
</div>
<div class="form-group">
<label>Nazwisko: </label>
<form:input class="form-control" path="lastName" />
<form:errors path="lastName"/>
</div>
<div class="form-group">
<label>Ulica: </label>
<form:input class="form-control" path="address.street" />
<form:errors path="address.street"/>
</div>
<div class="form-group">
<label>Miasto: </label>
<form:input class="form-control" path="address.city" />
<form:errors path="address.city"/>
</div>
<div class="form-group">
<label>Kod pocztowy: </label>
<form:input class="form-control" path="address.zipCode" />
<form:errors path="address.zipCode"/>
</div>
<input type="submit" class="btn btn-success" value="Zapisz" />
<a class="btn btn-primary"
href="${pageContext.request.contextPath}/user/list">Powrót do
listy</a>
</form:form>
</div>
</div>
</div>
</body>
</html>
SQL scripts
create table address (
id int(11) not null auto_increment,
street varchar(45) default null,
city varchar(45) default null,
zip_code varchar(45) default null,
primary key (id)
);
create table user (
id int(11) not null auto_increment,
first_name varchar(45) default null,
last_name varchar(45) default null,
address_id int(11) default null,
primary key (id),
KEY FK_ADDRESS_idx (address_id),
constraint FK_ADDRESS foreign key (address_id)
references address (id)
);
Informations from Console after save user and address to database:
Hibernate: insert into address (city, street, zip_code) values (?, ?, ?)
Hibernate: insert into User (address_id, first_name, last_name) values (?, ?, ?)
Information from Console after delete user and ?address? from database:
Hibernate: delete from User where id=?
After deleting the user from the table, there is no information about removing the address as above.
Upvotes: 0
Views: 1784
Reputation: 922
If you want to delete address value as well when you delete User data, then instead of using the query, you first need to fetch the User Object based on userId, then delete the object,
So, your code for deleting the user would look something like this:
public void delete(int id) {
User user = getOne(id); // method present in Abstract Dao class to find the object based on Id
Session session = sessionFactory.getCurrentSession();
session.delete(user);
}
Upvotes: 0
Reputation: 1518
Make
User
&Address
table bi-directional.In Address.java add below property with getter/setter
@OneToOne(mappedBy="address") // variable name of join property Address in User.java private Address address;
When In case we can't alter Address.java, you can use below approach for orphan removal.
In User.java
@OneToOne(cascade = CascadeType.ALL, orphanRemoval=true) @JoinColumn(name = "address_id") private Address address;
Upvotes: 1