I have the entity User, this entity is used twice in another entity Product. I use hibernate and foreign keys between tables are created. There are also two foreign keys - for create_by and modified_by.
class User
class User {
private Set<Product> products = new HashSet<Product>(0);
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
public Set<ProductgetProducts{
return this.products
}
public void setProducts(Set<Product> products) {
this.products = products
}
}
class Product
class Product {
private User createdBy;
private User modifiedBy;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinColumn(name = "modified_by", nullable = false)
public User getModifiedBy() {
return modifiedBy;
}
public void setModifiedBy(User createdBy) {
this.modifiedBy= modifiedBy;
}
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinColumn(name = "created_by", nullable = false)
public User getCreatedBy() {
return createdBy;
}
public void setCreatedBy(User createdBy) {
this.createdBy = createdBy;
}
}
It throws me this exception and I don't know how to fix it.
Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: Product.user in User.products
mappedBy = "user"means: I'm the inverse side of a bidirectional association, whose owner side is the field user in the target entity (i.e. Product). There is no user field in Product. So it doesn't make sense.
If you want the createdBy ad modifiedBy associations to be bidirectional, you need two fields in User:
#OneToMany(mappedBy = "createdBy")
private Set<Product> createdProducts;
#OneToMany(mappedBy = "modifiedBy")
private Set<Product> modifiedProducts;
Related
Here I am adding a list of new orders to a specific user. While adding orders to a specific user, it returns success status, but still my database is empty. Why the data is not getting added...?
Users
#Entity
Class Users{
#Id
private int id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private List<Orders> orders;
}
Orders
#Entity
Class Orders{
#Id
private int id;
private Date orderDate;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
}
Repository
public ResponseEntity<String> addOrder(int userId,List<Orders> orders) throws UserNotFound{
User user =userRepository.findById(userId).orElse(null);
if(user==null) throw new UserNotFound("User Not Found");
for(Orders o:orders){
o.setOrderDate(new Date("....."));
}
user.getOrders().addAll(orders);
userRepository.save(user);
}
Try to add cascade persist to your relation specification:
#Entity
Class Users{
#Id
private int id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade={CascadeType.PERSIST})
private List<Orders> orders;
}
I am running into a problem deleting related entities from my database. I have a trading application where users can post trades and express their interests in other people's trades.
When a user deletes their account, all trades posted and interests expressed by this user should be removed from the database. However, the latter doesn't seem to work (I am also not sure if the first one works as I don't know in what order they get executed). I get the error:
The DELETE statement conflicted with the REFERENCE constraint "FKq9kr60l7n7h3yf82s44rkoe4g". The conflict occurred in database "dbi438161_i438161", table "dbo.interests", column 'user_id'.
Note: I get the same when I try to delete a trade but then the column is 'trade_id'
I do the same for the trades and roles of a user so I think it has to do with what is in my interest entity. I am using CascadeType.ALL annotation to let Hibernate remove related entities
Lists of related entities in user:
#ManyToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinTable(name="user_roles",
joinColumns = { #JoinColumn(name = "user_id") },
inverseJoinColumns = { #JoinColumn(name = "role_id") })
private List<Role> roles = new ArrayList<>();
#Transient
#JsonIgnore
#OneToMany(cascade=CascadeType.ALL, mappedBy="user")
private List<Interest> interests = new ArrayList<>();
#Transient
#JsonIgnore
#OneToMany(cascade=CascadeType.ALL, mappedBy="user")
private List<Trade> trades = new ArrayList<>();
Interest entity:
#Entity
#Table(name = "interests")
public class Interest {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int interestId;
#ManyToOne
#JoinColumn(name = "user_id", nullable = false)
private User user;
#ManyToOne
#JoinColumn(name = "trade_id", nullable = false)
private Trade trade;
private String comment;
public Interest(User user, Trade trade, String comment) {
this.user = user;
this.trade = trade;
this.comment = comment;
}
public Interest(){
}
}
For comparison, the trade entity:
#Entity
#Table(name = "trades")
public class Trade {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="trade_id")
private int tradeId;
#Column(name="wants")
private String wants;
#Column(name="offers")
private String offers;
#Column(name="date_last_modified")
private LocalDateTime lastModified;
#ManyToOne
#JoinColumn(name = "user_id", nullable = false)
private User user;
#Transient
#JsonIgnore
#OneToMany(cascade=CascadeType.ALL, mappedBy="trade")
private List<Interest> interests = new ArrayList<>();
public Trade(String wants, String offers, User user){
this.wants = wants;
this.offers = offers;
this.user = user;
}
public Trade() {
}
}
Does anybody have an idea on what I am doing wrong here? Thanks in advance
Try to set orphanRemoval to true for the following associations:
#OneToMany(cascade=CascadeType.ALL, mappedBy="user", orphanRemoval = true)
private List<Interest> interests = new ArrayList<>();
#OneToMany(cascade=CascadeType.ALL, mappedBy="user", orphanRemoval = true)
private List<Trade> trades = new ArrayList<>();
As it is stated in the documentation:
If the child entity lifecycle is bound to its owning parent so that the child cannot exist without its parent, then we can annotate the association with the orphanRemoval attribute and dissociating the child will trigger a delete statement on the actual child table row as well.
Please also note that you should not use cascade=CascadeType.ALL for the #ManyToMany association as it explained in the documentation:
For #ManyToMany associations, the REMOVE entity state transition doesn’t make sense to be cascaded because it will propagate beyond the link table. Since the other side might be referenced by other entities on the parent-side, the automatic removal might end up in a ConstraintViolationException.
Spring Rest API application. So, when I delete one User(I want to delete also the orders for that user). The user id is foreign key for the order(one to many relation).
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id")
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#OneToMany(
mappedBy = "order_products",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<OrderHasProduct> orders = new ArrayList<>();
The user class
#OneToMany(
mappedBy = "orders",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<OrderHasProduct> orders = new ArrayList<>();
public boolean deleteUser(int id){
User usr = usrRepository.findById(id);
if (usr == null) {
throw new ResourceNotFoundException(User.class.getSimpleName());
}
usrRepository.delete(id);
User deletedUser = usrRepository.findById(id);
if (deletedUser != null)
return false;
return true;
}
Can you show the OrderHasProduct class as well?
Here’s an working example:
#Entity
public class Post {
#Id
#GeneratedValue
private Long id;
private String title;
#OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
//Constructors, getters and setters
}
#Entity
public class PostComment {
#Id
#GeneratedValue
private Long id;
private String review;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "post_id")
private Post post;
//Constructors, getters and setters
}
}
I have a error with Hibernate:
org.hibernate.AnnotationException: mappedBy reference
an unknown target entity property: ch.zkb.documenz.backend.model.Template.user
in ch.zkb.documenz.backend.model.User.templates
I have two Tables: User and Template, but in Template I need to use the id of the user in: createdBy, lockBy or lastUpdateBy, I think I have to use the #onetomany like in my example, but something it's incorrect, What is the best practice to do this then?
public class User implements Serializable {
#Id
#Column(name = "user_id", unique = true, nullable = false)
private Long id;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
private Set<Template> templates;
public class Template implements Serializable {
#Id
#Column(name = "template_id", unique = true, nullable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "createdBy")
private User createdBy;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "lastUpdateBy")
private User lastUpdateBy;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "lockBy")
private User lockBy;
EDIT, I have now a Problem with the Bidirectional LAZY load, I want to get the user wo created the template but I can't.. always is NULL, but in the DB is stored properly
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "createdBy")
private Set<Template> createdTemplates;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "createdBy", referencedColumnName = "user_id", nullable = false)
private User createdBy;
You schould map to your private keys seperately, then in an transient method you can merge them.
private Set<Template> templates = new HasSet<Template>();
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "createdBy")
private Set<Template> createdByTemplates;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "lastUpdateBy")
private Set<Template> lastUpdateByTemplates;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "lockBy")
private Set<Template> lockByTemplates;
#Transient
public Set<Template> getTemplates(){
if(getCreatedByTemplates() != null){
templates.addAll(getCreatedByTemplates());
}
if(getLockByTemplates() != null){
templates.addAll(getLockByTemplates());
}
if(getLastUpdateByTemplates() != null){
templates.addAll(getLastUpdateByTemplates());
}
return templates;
}
The exception message is clear about what doesn't work, the mappedBy must refer to the attribute name that you do have in the other entity,
in your Template entity there's no attribute named "user", try to remove the mappedBy.
I believe mapped by is looking for bean name "user" in Template.
public class User implements Serializable {
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
private Set<Template> templates;
public class Template implements Serializable {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;
Change is variable name createdby to user in Template Class would help
I didn't know how to describe my question in the title but I hope it will do.
So here is my situation.
I use hibernate to map my entities to db tables.
I got one entity like this:
#Entity
#Table(name = "EX.EXAMPLE")
public abstract class Entity
{
private CustomEntity customEntity;
public static final String CUSTOM_ENTITY = "customEntity";
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity()
{
return this.customEntity;
}
}
And my CustomEntity
#Entity
#Table(name = "EX.EXAMPLE2")
public class CustomEntity
{
private Entity entity;
public static final String ENTITY = "entity";
#OneToOne
#JoinColumn(name = "ID_ENTITY", nullable = true)
public Entity getEntity()
{
return this.ntity;
}
}
So here is my question: Is it possible to add another CustomEntity relation to Entity? And how do I map it?
Example what I mean:
#Entity
#Table(name = "EX.EXAMPLE")
public abstract class Entity
{
private CustomEntity customEntity;
public static final String CUSTOM_ENTITY = "customEntity";
private CustomEntity customEntity2;
public static final String CUSTOM_ENTITY2 = "customEntity2";
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity()
{
return this.customEntity;
}
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity2()
{
return this.customEntity2;
}
}
I only managed it by changing customEntity to a list in Entity.
Greetings
Yes, that is perfectly normal situation. You just need two fields with different mappedBy`, one for each relation
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY1, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity()
{
return this.customEntity;
}
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY2, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
#JoinColumn(name = "entity_2_id")
public CustomEntity getCustomEntity2()
{
return this.customEntity2;
}
And two fields in CustomEntity, one for each mapping
#OneToOne
#JoinColumn(name = "ID_ENTITY_1", nullable = true)
public Entity getEntity1()
{
return this.entity1;
}
#OneToOne
#JoinColumn(name = "ID_ENTITY_2", nullable = true)
public Entity getEntity2()
{
return this.entity2;
}