JPA delete bidirectional entity - java

I am having some problems with JPA. I am new at this topic so my question maybe is really stupid, but i hope some of you could point me to the right direction.
I have Project and User entity. Every user can have as many projects assign to him as it can.
I created the relationship bidirectional
User OneToMany -> Project,
Project ManyToOne -> User
My problem is that if i want to delete a user i want all the projects to be deleted as well, but i receive an error at that point:
Internal Exception: java.sql.SQLIntegrityConstraintViolationException:
DELETE on table 'USER_DATA' caused a violation of
foreign key constraint 'PROJECT_USERNAME' for key (Test Use1312r1).
The statement has been rolled back.
Error Code: -1
Call: DELETE FROM USER_DATA WHERE (USERNAME = ?)
bind => [1 parameter bound]
My User entity looks like this:
#Entity
#Table(name="USER_DATA", uniqueConstraints = #UniqueConstraint(columnNames = {"USERNAME", "link"}))
public class User implements Serializable {
#Column(name="USERNAME")
#Id
#NotNull
private String name;
#Column(name="USERROLE")
#Enumerated(EnumType.STRING)
private UserRole role;
private String password;
private String link;
// Should be unique
private String session;
#OneToMany(mappedBy="user", cascade=CascadeType.ALL)
private Collection<Project> projects;
My Project Entity like this:
#Entity
#Table(name="PROJECT")
#XmlRootElement
public class Project implements Serializable {
#Id
private int id;
private String name;
private String description;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="START_DATE")
private Date beginDate;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="END_DATE")
private Date endDate;
#ManyToOne
#JoinColumn(name="USERNAME", nullable=false,updatable= true)
private User user;
And my BL:
public User getUser(String userName) throws NoDataFoundException {
EntityManager em = DbConnection.getInstance().getNewEntity();
try {
User user = em.find(User.class, userName);
if (user == null) {
throw new NoDataFoundException("User is not found in the DB");
}
return user;
} finally {
em.close();
}
}
public void deleteUser(String userName) throws ModelManipulationException {
EntityManager em = DbConnection.getInstance().getNewEntity();
try {
User userToBeDeleted = getUser(userName);
em.getTransaction().begin();
userToBeDeleted = em.merge(userToBeDeleted);
em.remove(userToBeDeleted);
em.getTransaction().commit();
} catch (Exception e) {
throw new ModelManipulationException(
"Error in deleting user data for username" + userName
+ "with exception " +e.getMessage(),e);
}
finally{
em.close();
}
}
Thanks in advance guys.

after the merge call, are there any Projects in userToBeDeleted.projects? I suspect there are none, which prevents any from being deleted. Cascade remove can only work if you populate both sides of bidirectional relationships, so check that when you associate a user to a project, you also add the project to the user's project collection.

Related

Why unidirectional #OneToMany update is not working

I am completely new to JPA and ORM concept, so I am hoping someone can lucidly explain what might be the problem with my code.
#Entity
#Table(name = "PERSISTENCE_customer")
public class Customer implements Serializable {
private static final long serialVersionUID = 1005220876458L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
#OneToMany (cascade = CascadeType.ALL, orphanRemoval = true)
private List<CustomerOrder> orders;
}
#Entity
#Table(name = "PERSISTENCE_ORDER")
public class CustomerOrder implements Serializable{
private static final long serialVersionUID = 199102142021L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
String status;
#NotNull
#OneToMany (cascade = CascadeType.ALL, orphanRemoval = true)
private List<LineItem> lineItems = new ArrayList();
#NotNull
private String orderNumber;
................
................
}
#Entity
#Table(name = "PERSISTENCE_LINEITEM")
public class LineItem implements Serializable {
private static final long serialVersionUID = 1991217202100959L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
private Integer quantity;
#NotNull
private Part part;
}
Initially, the Customer entity is created through the user interface and persisted successfully. Later, the customer has an order and I update the Customer with CustomerOrder as follow:
private void UpdateCustomer(Customer customer) {
FacesContext fc = FacesContext.getCurrentInstance();
List<ShoppingCartItem> shoppingCart = getShoppingCart();
CustomerOrder order = new CustomerOrder();
List<CustomerOrder> orders = customer.getOrders();
order.setLastUpdated(new Date());
order.setOrderNumber(getInvoiceNumber());
List<LineItem> lineItems = shoppingCart
.stream()
.map(e -> (new LineItem(e.getPart(), e.getQuantity())))
.collect(Collectors.toList());
order.setLineItems(lineItems);
order.setStatus("Pending Shipment");
order.setTotal(getTotal());
orders.add(order);
customer.setOrders(orders);
try {
updateOrders(customer, orders);
fc.addMessage(null,
new FacesMessage("Customer order added successfuly"));
} catch (ListServiceException e) {
FacesMessage errMsg = new FacesMessage(FacesMessage.SEVERITY_FATAL,
"Error while adding customer order: ", e.getMessage());
fc.addMessage(null, errMsg);
}
}
private void updateOrders(Customer cust, List<CustomerOrder> orders) throws ListServiceException {
try { //em is the EntityManager injected as the field member
if (em != null) {
if (em.isOpen()) {
Customer c = getCustomer(cust.getId());
c.setOrders(orders);
em.merge(c);
} else {
logger.severe("Entity manager is closed");
}
else {
logger.severe("Entity manager is NULL");
}
} catch (Exception e) {
throw ThrowListServiceException.wrapException(e);
}
}
Once the EntityManage merges I get the following exception. I was under the impression that I don't need to explicitly persist the LineItem and CustomerOrder entities myself. I thought that the JPA will persist all the entities in the object graph. Why am I getting this exception? (I am using GlassFish 5.1 server with EclipseLink JPA)
Thanks in advance.
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: Column 'ORDERS_ID' cannot accept a NULL value.
Error Code: 30000
Call: INSERT INTO PERSISTENCE_CUSTOMER_PERSISTENCE_ORDER (orders_ID, Customer_ID) VALUES (?, ?)
bind => [2 parameters bound]
Query: DataModifyQuery(name="orders" sql="INSERT INTO PERSISTENCE_USER_PERSISTENCE_ORDER (orders_ID, User_ID) VALUES (?, ?)")
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:331)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:905)
...............................
.................................
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'ORDERS_ID' cannot accept a NULL value.
Update
Using the IDE (Netbeans) debugger, I stepped through the code, and as I predicted, during the entity merge the JPA does not add new entities that are part of the object graph to the persistence context. For instance, in the updateOrders() method when I try to update the existing Customer object with a list of new CustomerOrder object, the JPA doesn't figure out that the elements in the List are not part of the persistence context yet and they need to be added. As a result, I had to modify my code to first add the List to the persistence context and then merge the Customer object with the newly persisted List. By doing so I no longer get the exception.
By the way, at moment, all the mapping relationships are unidirectional because I didn't see any reasons to use bidirectional mapping. However, would I gain anything by making these mappings bidirectionals?
Your OneToMany mapping is missing join specification or mappedBy value
I noticed that.
Firstly You should commit new order to database.Then you should link it with user.I'm not sure if this solves your problem but this is a problem.Can you check it ?
In my opinion, if you keep customer information in your Order entity, it may solve this problem.
#ManyToOne ()
private Customer customer;
And in your Customer entity you should put mappedBy=customer for orders field.
After that, instead of giving orders for customer, you can give customer for a specific order. In my opinion it will achieve a better relationship mapping;
order.setCustomer(customer);
I hope i understood it right and this will solve your problem. When you give customer detail for order, you dont need to give orderlist detail for the same customer. Only one of them should be enough.

Springboot PUT request fails when updating nested Json object

I have a customer object and inside that customer object i have a login object which contains username and password. When i do a POST the request works fine however when i try to do a PUT request it fails. It fails because it says Duplicate entry on the username.
I would like to be able to update the customer details without having to change the username. How can i achieve this.
This is my code :
UserLogin Entity :
#Entity
#Table(name = "Customer",
uniqueConstraints =
{
#UniqueConstraint(columnNames = "email"),
#UniqueConstraint(columnNames = "id"),
#UniqueConstraint(columnNames = "phoneNumber")
}
)
public class Customer implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int customerNumber;
#OneToOne(cascade= CascadeType.ALL)
#JoinColumn(name = "loginCredentialsID")
private UserLogin userlogin;
private String phoneNumber;
private String email;
private String physicalAddress;
private String country;
... getters and setters
}
UserLogin Entity :
#Entity
#Table(name = "UserLogin",
uniqueConstraints =
{
#UniqueConstraint(columnNames = "userName")
})
public class UserLogin implements Serializable, UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int loginCredentialsID;
private String username;
private String password;
... getters and setters
}
CustomerService Class :
public Response updatCustomeretails(int id,Customer customer) {
customer.setCustomerNumber(id);
if( customer== null ){
throw new ResourceNotFoundException("Empty", "Missing Data");
}else {
customerRepository.save(customer);
return new Response(" Customer Updated Successfully","Thank you ");
}
When using Sping data JPA to update you should use save which you correctly did when saving on this line customerRepository.save(customer);. However when persisting data to a database in a PUT request JPA uses the keys within your entity mappings to be able to update the proper record.
So in your case you get that error when JPA tries to save a new record rather than an update to an existing record. Your intent is to update but I suspect your keys are missing or they are not properly defined so JPA tries to go and save a new record instead of updating.
So when you do the update(PUT) make sure the object you are passing has the same keys as the one you want to update.

Spring how to manage a mapping table (Many to Many)

I am developing an app where the user can have one or more roles, for this I decided to created a mapping (intermediate) table, so I ended with User, Role and UserRole like this:
In this app the role(s) a user has determines wheneaver he can access certain views or actions in the frontend. The only thing I need is to retrive the roles a user has and add/delete them. JPA Tools created he following EJB for me (simplified):
USER
/**
* The persistent class for the usuario database table.
*
*/
#Entity
#NamedQuery(name="Usuario.findAll", query="SELECT u FROM Usuario u")
public class Usuario implements Serializable {
private static final long serialVersionUID = 1L;
private int idUsuario;
private List<RolUsuario> rolUsuarios;
public Usuario() {
}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public int getIdUsuario() {
return this.idUsuario;
}
public void setIdUsuario(int idUsuario) {
this.idUsuario = idUsuario;
}
//bi-directional many-to-one association to RolUsuario
#OneToMany(mappedBy="usuario")
public List<RolUsuario> getRolUsuarios() {
return this.rolUsuarios;
}
public void setRolUsuarios(List<RolUsuario> rolUsuarios) {
this.rolUsuarios = rolUsuarios;
}
public RolUsuario addRolUsuario(RolUsuario rolUsuario) {
getRolUsuarios().add(rolUsuario);
rolUsuario.setUsuario(this);
return rolUsuario;
}
public RolUsuario removeRolUsuario(RolUsuario rolUsuario) {
getRolUsuarios().remove(rolUsuario);
rolUsuario.setUsuario(null);
return rolUsuario;
}
}
USER_ROLE
/**
* The persistent class for the rol_usuario database table.
*
*/
#Entity
#Table(name="rol_usuario")
#NamedQuery(name="RolUsuario.findAll", query="SELECT r FROM RolUsuario r")
public class RolUsuario implements Serializable {
private static final long serialVersionUID = 1L;
private int idRol_Usuario;
private Usuario usuario;
private Rol rol;
public RolUsuario() {
}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public int getIdRol_Usuario() {
return this.idRol_Usuario;
}
public void setIdRol_Usuario(int idRol_Usuario) {
this.idRol_Usuario = idRol_Usuario;
}
//bi-directional many-to-one association to Usuario
#ManyToOne(fetch=FetchType.LAZY)
public Usuario getUsuario() {
return this.usuario;
}
public void setUsuario(Usuario usuario) {
this.usuario = usuario;
}
//bi-directional many-to-one association to Rol
#ManyToOne(fetch=FetchType.LAZY)
public Rol getRol() {
return this.rol;
}
public void setRol(Rol rol) {
this.rol = rol;
}
}
In my project I am using the EJB to create POJO for the frontend. When I ask for the full list of roles for a given user how should I go about doing this:
Create a UserRole repository using CrudRepository with a metod like
List<RolUsuario> findByUsuario(Usuario user);
Return the list of UserRole to my User Service and go over the
the list extracting each Role into a UserPOJO
Send to frontend.
Or is there any way to just get right off the bat a list of Roles in table UserRole where User(Id?) = something?
This is hard to describe for me. My app only cares for the roles of a user, not the mapping table entity, so the botton line is that somehow I have to get them but I don't know where to start.
Any pointers would be extremely useful.
Edit:
Or I could...
Create UserRole for new role addition to a user.
Adding UserRole to the List inside User.
To get the roles of a user get the UserRolelist instead.
Your schema for User and Role is not commonly used. I advice to you make a #ManyToMany association from a user to roles. If you will need to map a join table to the entity (unlikely) you can do it later. And, please, use English identifiers and Java naming convention (idRol_Usuario). It will help you and others.
#Entity
#Table
public class User {
#Id
#GeneratedValue
private Long pid;
#ManyToMany(fetch = FetchType.LAZY)
private List<Role> roles;
}
#Entity
#Table
public class Role {
#Id
private Long pid;
#Column
private String name;
}
You can use Set<Role> too
#ManyToMany(fetch = FetchType.LAZY)
private Set<Role> roles;
The roles of a User and in the rolUsuarios list. In your User service, look up a user, often with the id. If you have the idUsuario, then user EntityManger.find(Usuario.class, idUsuario) and you can read the user roles by getRolUsuarios, or at least that would be the typical way to do it.
In your table design you have a id for the user_role table (iduserrole), which is not typical for a join table. Typically, you just create your entity with a OneToMany annotation and the join table is created for you:
#Entity
public class User {
#Id #GeneratedValue private Long id;
#ManyToMany
Set<Role> roles;
}
and
#Entity
public class Role {
#Id #GeneratedValue private Long id;
}
These two classes would create three tables, the User table, the Role table, and the User_Role Join Table. The join table would have the each id from the user and the role tables in it, and nothing more.
EDIT: The roles is changed to ManyToMany because otherwise a constraint will be added to the database that will prevent the a Role from being added to more than one user. Typically the role table has only unique roles in it, e.g., USER, ADMIN, etc., and so you want to be able to assign them to more than one user.
Is this what you are looking for?

Java EE Query two tables

So here is my problem:
I have two tables: User and Book, they are in ManyToOne relation. The Book table has attribute called user_id that connects both tables.
Using Eclipse I generated entity classes and work on them without problem until now.
The problem is, when I want to get "books" that have speciffic user_id I get an error:
java.lang.IllegalArgumentException: Parameter value [1] did not match expected type [model.User (n/a)]
The "value" is an id that I'm getting from session, I tried it in both int and String.
part of BookDao:
public List<Book> getFullListWithId(Integer id) {
List<Book> list = null;
Query query = em.createQuery("select b from Book b WHERE b.user = :id");
if (id!= null) {
query.setParameter("id", id);
}
try {
list = query.getResultList();
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
part of BookBB:
public List<Book> getFullListWithId(){
HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true);
Integer str =(Integer)session.getAttribute("userId");
return bookDAO.getFullListWithId(str);
}
part of Book.java
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id_offer")
private int idOffer;
private String adress;
private String contact;
#Column(name="is_ready")
private String isReady;
private String name;
private String others;
private int price;
private int rooms;
private String size;
private String surname;
//bi-directional many-to-one association to User
#ManyToOne
#JoinColumn(name="user_id")
private User user;
part of User.java
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id_user")
private int idUser;
private String login;
private String password;
//bi-directional many-to-one association to Book
#OneToMany(mappedBy="user")
private List<Book> books;
Thank you so much for any help possible.
You have two options.
1) you pass an User object with the id as a parameter to the query (I prefer that solution):
Query query = em.createQuery("select b from Book b WHERE b.user = :user");
User u = new User();
u.setId(id)
query.setParameter("user", u);
2) you pass the id as a parameter:
Query query = em.createQuery("select b from Book b WHERE b.user.id = :id");
if (id!= null) {
query.setParameter("id", id);
}
Please note the b.user.id in the query
I believe query should be select b from Book b WHERE b.user.idUser = :id
You might want to take a look at hibernate docs
https://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/queryhql.html {Chapter 16}

Persisting class gives null-value on ID

I'm trying to persist a User-class, which contains 2 lists: one with users to see which are following that specific user, and one with users to see which users that specific user follows.
User.java:
#Entity
#XmlRootElement
#Table(name="Users")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String web;
private String bio;
#OneToMany(cascade=CascadeType.ALL)
private Collection<User> followers = new ArrayList();
#OneToMany(cascade=CascadeType.ALL)
private Collection<User> following = new ArrayList();
I have a class named UserDAOJPA.java which creates and modifies the users:
#Alternative
#Stateless
public class UserDAOJPA implements Serializable, IUserDAO {
#PersistenceContext(unitName = "KwetterSOAPPU")
private EntityManager em;
#Override
public void create(User user) {
em.persist(user);
}
#Override
public List<User> getFollowers(User user) {
List<User> followers;
followers = (List<User>) user.getFollowers();
return followers;
}
#Override
public void addFollower(User user, User follower)
{
user.addFollower(follower);
em.merge(user);
}
#Override
public List<User> getFollowing(User user) {
List<User> following;
following = (List<User>) user.getFollowing();
return following;
}
#Override
public void addFollowing(User user, User following) {
user.addFollower(following);
em.merge(user);
}
#PostConstruct
private void initUsers() {
User u1 = new User("Hans", "http", "geboren 1");
User u2 = new User("Frank", "httpF", "geboren 2");
User u3 = new User("Tom", "httpT", "geboren 3");
User u4 = new User("Sjaak", "httpS", "geboren 4");
this.create(u1);
this.create(u2);
this.create(u3);
this.create(u4);
this.addFollowing(u1, u2);
this.addFollower(u2, u1);
this.addFollowing(u1, u3);
this.addFollower(u3, u1);
this.addFollowing(u1, u4);
this.addFollower(u4, u1);
My own guess is that I'm missing a correct annotation in the User.java class, when looking at the Collection of User's.
The error message:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: Column 'FOLLOWING_ID' cannot accept a NULL value.
Error Code: -1
Call: INSERT INTO Users_Users (followers_ID, User_ID) VALUES (?, ?)
bind => [2 parameters bound]
Query: DataModifyQuery(name="followers" sql="INSERT INTO Users_Users (followers_ID, User_ID) VALUES (?, ?)")
Relationship between users and their followers is a bidirectional many-to-many relationship, not two one-to-many relationships, therefore you need to map it as such:
#ManyToMany(mappedBy = "following")
private Collection<User> followers = new ArrayList<>();
#ManyToMany
#JoinTable(name = "followers",
joinColumn = #Column(name = "follower_id"),
inverseJoinColumn = #Column(name = "following_id"))
private Collection<User> following = new ArrayList<>();
Also note that cascading usually should be used when referring to logically "owned" entities. In your case it makes no sense because Users don't "own" each other.
Just guessing, as I don't have an environment to test the code, but I think you need to change the create method to return the return value from the EntityManager.merge class and use the result, like this:
...
public User create(User user) {
return em.merge(user);
}
...
User user = this.create(new User(...));
User follower = this.create(new User(...));
this.addFollower(user, follower);
The reason for the error is that the reference to the Follower's id is undefined, as you don't return the merged object state one you've persisted it.

Categories