#ManyToOne and #OneToOne relations with #EmbeddedId - java

I am trying to change the id of my database entities from a single long to a composite id consisting of two long's which are encapsulated in my ID.class shown below. What annotations would you use for ManyToOne and OneToMany relations? Did I made a mistake with my annotations, especially with #JoinColumns, #Embeddable and #EmbeddedId. My problem is that relations are stored with null;
My ID class consist of an autogenerated long (pid) and a manually set secondary long (sid). This is the ID for all my entity classes:
#Embeddable
public class ID implements Serializable {
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long pid;
#Column(nullable=false)
private long sid;
}
My Entity-class (example) has a many-to-one relation to Office a one-to-one relation to Settings (both relations are uni-directional).
#Entity
#Table(name="user")
public class User {
#EmbeddedId
private ID id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "office_pid", referencedColumnName = "pid", updatable=false, insertable=false),
#JoinColumn(name = "office_sid", referencedColumnName = "sid", updatable=false, insertable=false)
})
private Office office;
#OneToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE}, fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "setting_pid", referencedColumnName = "pid", updatable=false, insertable=false),
#JoinColumn(name = "setting_sid", referencedColumnName = "sid", updatable=false, insertable=false)
})
private Settings setting;
#OneToMany(mappedBy = "user", cascade=CascadeType.REMOVE, fetch=FetchType.LAZY)
private List<Account> accounts= new ArrayList<>();
}
Office class (Many-To-One example):
#Entity
#Table(name = "office")
public class Office {
#EmbeddedId
private ID id;
#Column(length = 128)
private String description;
}
Account class (One-To-Many example):
#Entity
#Table(name="account")
public class Account {
#EmbeddedId
private ID id;
#Column(length = 16)
private String description;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "user_pid", referencedColumnName = "pid"),
#JoinColumn(name = "user_sid", referencedColumnName = "sid")
})
private User user;
}
Thanks in advance for your help.

Sequencing shouldn't be used if you want a composite pk - use only the sequence generated field which is unique, or composite business fields that are unique.
Second issue is the joinColumn definitions in User. You have marked them as updatable=false, insertable=false which means the foreign keys cannot be changed through the mappings, and is why they are always null regardless of setting the relationship. Remove the updatable=false, insertable=false settings from each to have the fields set when you set the relationship.

Related

can't find the pattern of setting cascade logic

I have 3 tables which are Person Login and Account.
Person and Login is OneToOne relation and Login has one FK which is connected Person's id column called PERSON_ID.
Person(one) and Account(many) is OneToMany relation and Account has one FK which is connected Person's id column called PERSON_ID as well .
what i want to do is when i delete one data from Account , nothing happen to Person and Login.
if i delete one data from Person which id=1, Login's PERSON_ID=1 data will be deleted , and all of the data PERSON_ID=1 from Account will be deleted as well.
if i delete one data from Login which PERSON_ID=1, Person 's id=1 data will be deleted , and all of the data PERSON_ID=1 from Account will be deleted as well.
how should i set the cascade ?
i've tried dozens of times and still can't find the logic in there, thanks!!
here's my code of all 3 tables without setting cascade:
`
#Entity
#Table(name = "PERSON")
public class Person {
#Id
#Column(name = "ID", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "NAME")
private String name;
#Column(name = "SEX")
private String sex;
#OneToMany(mappedBy = "person",fetch = FetchType.LAZY)
private List<Account> account;
#OneToOne(mappedBy = "person")
private Login login;
#get..
#set..
}
`
#Entity
#Table(name = "ACCOUNT")
public class Account {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "ACCOUNT")
private String account;
#Column(name = "AMOUNT")
private String amount;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PERSON_ID",referencedColumnName = "ID")
public Person person;
#get..
#set..
}
`
#Entity
#Table(name = "LOGIN")
public class Login {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private long id;
#Column(name = "USERNAME")
private String userName;
#Column(name = "PASSWORD")
private String password;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PERSON_ID", referencedColumnName = "ID")
private Person person;
#get..
#set..
}
It's been a while, but if I'm not mistaken you need to use the cascade=REMOVE option on the OneToMany and OneToOne relationships. In the OneToOne I think you need to specify cascade=REMOVE on the side that does NOT own the relationship, that is, the side that also contains the "mappedBy" property.
Finally, I believe JPA will NOT automatically load lazy relationships and then cascade them. I'm thinking you may need to fetch the relationship before you delete the parent entity (otherwise JPA will not know what to delete).

HIbernate ignore fetching data from OnetoMany field

I would like to ignore #OnetoMany field in my entity. fetch data need to get actual fields but don't want to fire query to dependent table. But deleting data from parent table needs deletion from dependent table
I have tried #Transient that ignores but the delete is also being ignored. Is there any other option to tell JPA not to fetch data from childs table when i call the parent entity?
#Entity
Table(name = "User")
public class UserEntity implements Serializable {
#Id
#Column(name = "id")
private int id;
#Column(name = "SERIAL", unique = true, nullable = false)
private String serial;
#OneToMany(mappedBy = "serialBySerialId", cascade = CascadeType.ALL)
private Set<UserActionEntity> userActionsById;
}
#Table(name = "user_action")
public class UserActionEntity implements Serializable {
#Id
#Column(name = "id")
private int id;
#Column(name = "action")
private String action;
#ManyToOne
#JoinColumn(name = "USER_ID", referencedColumnName = "ID", nullable = false)
private UserEntity userByUserId;
If you don't want to fire query to dependent table, you can use (fetch = FetchType.LAZY) on UserActionEntity property.
#OneToMany(mappedBy = "serialBySerialId", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<UserActionEntity> userActionsById;

Is there a way to map child entity to parent entity using parent entity field (not parent object)?

I'm trying to model one to many unidirectional association. I have two entities the first one (parent) is:
#Entity
#Table(name = "customer")
public class Customer{
#Id
#Column(name = "ID")
private Long id;
The second entity has many to one relation to Customer:
#Entity
#Table(name = "address")
public class Address{
#Id
#Column(name = "ID")
private Long id;
#ManyToOne(optional = false)
#JoinColumn(name = "CUSTOMER_ID", referencedColumnName = "ID")
private Customer customer;
I need to substitute property Customer in Address class by customer.id, is it possible?
You can use customerId field in Adress entity and join it manually with JPQL but it's not recommended and not creates foreign key.
#Entity
#Table(name = "address")
public class Address{
#Id
#Column(name = "ID")
private Long id;
#Column(name = "CUSTOMER_ID")
private Long customerId;

I have to persist a Map<EntityType, List<EntityType>> with JPA

I'm trying to persist an Entity that has a Map as one of its values. To be more precise. I have the #Entity Request that have a compound primary key with three elements. This primary key is composed by an id, the User an Map<EntityType, List<EntityType>> where the first EntityType is the selected service and the related value is the list of the items where the service will be applied to.
Below the code that I have but I'm missing the annotation that i have to use for the Map. I read online that the good way to go is the create a wrapper entity like to one that i created (SelectedService2MyItem) that holds the list and then the map is just a key-value pair between two entity but I can't make it works and I don't know how to proceed.
Does anyone can help me?
Request Entity
#Entity
public class Request {
#EmbeddedId
private RequestId id;
#Column
private String name;
#ManyToOne
#JoinColumn(name = "user_id", foreignKey = #ForeignKey(name = "FK_user_id"), nullable=false)
private User user;
//Getter, setter, constructor omitted
}
RequestId
#Embeddable
public class RequestId {
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "user_id", foreignKey = #ForeignKey(name = "FK_user_id"), nullable=false)
private User user;
private Map<ServiceOffered, SelectedService2MyItem> service2MyItem = new HashMap<ServiceOffered, SelectedService2MyItem>();
//Getter, setter, constructor omitted
}
SelectedService2MyItem
#Entity
public class SelectedService2MyItem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", updatable = false, nullable = false)
private Long id;
#OneToMany(mappedBy = "id")
private List<MyItem> myItemsSelected;
//Getter, setter, constructor omitted
}

Hibernate Bidirectional one-to-many with composite key

I am trying to create a bidirectional one-to-many association between two entities where the many side has a compound key. And one of the keys of the many side is coming from the one side. Also, I need to have the many side the owner of the association. Below is an example code showing what my code looks like.
Without Jointable
Parent Class which is the one side. I need to have this side owner of the association.
public class parent{
#Id
#Column(name = "NAME")
private String name;
#OneToMany(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="NAME", nullable = false),
#JoinColumn(name="PARENT", nullable = false)})
private Set<Child> childs;
}
Child Class which is the many side. Its primary key is "name" and "parent". "parent" is coming from the association.
public class child{
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name="parent", column=#Column(name="PARENT", nullable=false)),
#AttributeOverride(name="name", column=#Column(name="NAME", nullable=false))})
private ChildId id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="PARENT", nullable = false, updatable = false, insertable = false),
#JoinColumn(name="NAME", nullable = false, updatable = false, insertable = false)})
private Parent parent;
}
ChildId is the Embedded id.
#Embeddable
public class childId{
#Column(name = "PARENT")
private String parent;
#Column(name = "NAME")
private String name;
}
With Jointable
Parent Class
public class parent{
#Id
#Column(name = "NAME")
private String name;
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name="PARENTCHILD",
joinColumns= {#JoinColumn(name="PNAME", referencedColumnName = "NAME", nullable = false)},
inverseJoinColumns = {
#JoinColumn(name="CNAME", referencedColumnName = "NAME", nullable = false),
#JoinColumn(name="CPNAME", referencedColumnName = "PARENT", nullable = false)})
private Set<Child> childs;
}
Child Class
public class child{
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name="parent", column=#Column(name="PARENT", nullable=false)),
#AttributeOverride(name="name", column=#Column(name="NAME", nullable=false))})
private ChildId id;
#MapsId("parent")
#ManyToOne(fetch=FetchType.LAZY)
#JoinTable(name="PARENTCHILD",
inverseJoinColumns = {#JoinColumn(name="PNAME", referencedColumnName = "NAME", nullable = false)},
joinColumns = {
#JoinColumn(name="CNAME", referencedColumnName = "NAME", nullable = false),
#JoinColumn(name="CPNAME", referencedColumnName = "PARENT", nullable = false)})
private Parent parent;
}
Question1:
This code doesn't work. In case of "without jointable", it gives the below exception.
Caused by: org.hibernate.AnnotationException: A Foreign key refering com.entity.Parent from com.entity.Child has the wrong number of column. should be 1
Question2:
And in case of "with jointable", it gives below exception:
SQLCODE=-530, SQLSTATE=-23503, SQLERRMC=PARENTCHILD.FK_PARENTCHILD_CHILD
You don't need to keep Parent name as Id separately in Child , Hibernate will do that for you . I have made a simpler design .And you can control the relation by using mappedBy = childs ,in the #ManyToOne or mappedBy = parent in the #ManyToOne side.
#Entity
public class Parent{
#Id
private String name;
#OneToMany(fetch= FetchType.LAZY)
private Set<Child> childs;
public Parent(String name) {
this.name = name;
}
public Parent(){}
}
#Entity
public class Child{
#Id
private String name;
#ManyToOne(fetch=FetchType.LAZY)
private Parent parent;
}
Three tables will be generated by Hibernate
Child Table with columns name(Primary Key), parent_name(Foreign Key)
Parent Table with one column name(Primary Key)
Parent_child table with two columns parent_name and child_name
EDIT : Solution changed as per amir's needs, just add mappedBy whichever side you need to control the relationship.
#Entity
public class Child implements Serializable {
#Id
private String name;
#Id
#ManyToOne(fetch=FetchType.LAZY)
private Parent parent;
}
#Entity
public class Parent{
#Id
private String name;
#OneToMany(fetch= FetchType.LAZY)
private Set<Child> childs;
public Parent(String name) {
this.name = name;
}
public Parent(){}
}
EDIT - To name column in child side
#Id()
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="xyz")
private Parent parent;
To use One-To-Many bidirectional mapping using join table,use following code:
Parent Class
public class parent{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
.....
.....
#OneToMany(cascade=CascadeType.ALL)
#JoinTable(name="Parent_Child", joinColumns={#JoinColumn(name ="parentId", referencedColumnName ="id")},
inverseJoinColumns={#JoinColumn(name ="childId", referencedColumnName ="id")})
private Set<Child> children;
.....
.....
}
Child Class
public class Child{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
.....
.....
#OneToOne(cascade=CascadeType.ALL)
#JoinTable(name="Parent_Child", joinColumns={#JoinColumn(name ="childId", referencedColumnName ="id")},
inverseJoinColumns={#JoinColumn(name ="parentId", referencedColumnName ="id")})
private Parent parent;
.....
.....
}
Note that I have used OneTOMany mapping in Parent class - cause as per your logic a parent can have multiple children and I have used OneToOne mapping in Child class cause one child will have one parent(as mentioned in your requirements).
Hope this helps

Categories