I have 2 classes - Person and Team.
One person can be in only one team, but one team can have many people (person objects). The problem is that both Person and Team need the other class objects to create themselves.
Classes:
#Entity
#Table(name = "Team")
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private int id;
#Column(name = "teamName", nullable = false)
private String teamName;
#OneToMany(mappedBy = "myTeam", fetch = FetchType.LAZY)
private Set <Person> setOfMembers = new HashSet<Person>();
public Team(int id, String teamName, Set <Person> setOfMembers) {
//usual code (don't want to waste so much space)
}
... other irrelevant fields ...
}
#Entity
#Table(name = "Person")
public class Person{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", nullable = false)
private int id;
#Column(name = "teamID", nullable = false)
private int teamID;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "teamID", referencedColumnName = "id")
private Team myTeam;
public Person(int id, int teamID, Team myTeam) {
//usual code
}
... other irrelevant fields ...
}
Now in the main file, i'm trying to create a new object:
Team t1 = new Team(1,"Nights Watch", <and here i need Person Object, so i
do something as follows:> new Person(1,1, <and here i need Team object>));
so it goes recurrent, without end.
I want to add some records to the database, but i don't know how.
Actually I need there the Set of Person Objects, but still, i don't know how
Why do you need all your teammates during the initialization of the Team class ?
Just do a constructor without the Person argument, and then do a method like:
public void addTeamMember(Person person) {
setOfMembers.add(person);
}
Then, you can create the Team, create the Person and finally add the Person to the Team.
Related
Student.java
#Entity
#Table(name = "Student")
#Data
#NoArgsConstructor
public class Student implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "studentId", cascade = CascadeType.ALL)
private List<Subject> subjectList = new ArrayList<>();
public void addSubject(Subject subject) {
subjectList.add(subject);
subject.setStudentId(this);
}
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
}
Subject.java
#Entity
#Table(name = "Subject")
#Data
#NoArgsConstructor
public class Subject implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="studentId", nullable = false)
private Student studentId;
#Column(name = "subjectName")
private String subjectName;
#Column(name = "subjectCode")
private int subjectCode;
}
SubjectRepository.java
#Repository
public interface SubjectRepository extends JpaRepository<Subject, Long> {
}
As shown in the code structure above, I have 2 entities (Student, Subject) and a repository class (SubjectRepository). When i try to save into the Subject table, somehow the student name "Thomas" from the Student table gets updated as well in the database. I would like to only insert into Subject table without having any values from the Student table getting updated. Need some help on this. Thanks!
public static void main(String[] args) {
#Autowired protected SubjectRepository subjectRepository;
Student student = new Student();
student.setFirstName("Thomas");
Subject subject = new Subject();
subject.setSubjectName("Chemistry");
subject.setSubjectCode(12345);
student.addSubject(subject)
subjectRepository.save(subject)
}
I would like to only insert into Subject table without having any values from the Student table getting updated
You can achieve this with following code :
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="studentId", nullable = false, updatable = false)
private Student studentId;
When using Spring JPA I would suggest using the JpaRepository API. You just need to pass in your entity, and it should save as desired.
Ex:
subjectRepository.save(subject);
You have try this
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name="studentId", nullable = false, updatable = false)
private Student studentId;
#MaxExplode You have to use cascade = CascadeType.REFRESH then other details will not update. but if you are try to set updateStudent.setfirstNamr(student.getFirstName()); and then save parent object then i will update. otherwise it will not update.
I have a scenerio where one staff can belong to multiple organisation and for each organisation he can have different role. How can i map this in jpa?
Staff.java
public class Staff {
#ManyToMany
#JoinTable(name="STAFF_ORGANIZATION",joinColumns=#JoinColumn(name="staff_id"),inverseJoinColumns=#JoinColumn(name="organization_id"))
private Set<Organization> organizations;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
}
Organization.java
public class Organization {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column
private String OrganizationName;
#ManyToMany(mappedBy="organizations")
private Set<Staff> staff;
}
StaffRoles.java
public class StaffRoles {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column
#Enumerated(EnumType.ORDINAL)
private Roles roles;
public enum Roles {
USER(100), ADMIN(200);
private int values;
Roles(int values) {
this.values = values;
}
public int getValues() {
return values;
}
}
Can anyone please help me in mapping the roles to the staff. So many staff can belong to many organisation and for each organisation he can have different role.
Any help will be highly appreciated!
Althought this question is not written clearly I will answer your question based on what I have understood.
Below is an ER-diagram for how your tables might look like.
Now you just need to create the classes needed.
User.java
Organization.java
UserOrganization.java
UserRole.java
Connect the right instance variables now via ManyToMany and OneToOne to achieve your goal.
EDIT:
After the question has been updated with more specific information, I can aid more in this answer. First make a StaffOrganizationRoles class that will sit between StaffOrganization and Roles. Next, make StaffOrganization sit between Staff and Organization, which means that instead of ManyToMany it will be ManyToOne from Staff -> StaffOrganization, and ManyToOne from Organization > StaffOrganization.
public class StaffOrganizationRoles {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "stafforganization_id", unique = false, nullable = false)
private StaffOrganization user;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "role_id", unique = false, nullable = false)
private Role role;
}
Staff organization class:
public class StaffOrganization {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "staff_id", unique = false, nullable = false)
private Staff staff;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "organization_id", unique = false, nullable = false)
private Organization organization;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "staffOrganization")
private Set<StaffOrganizationRoles> staffOrganizationRoles = new HashSet<>(0);
}
I've edited the picture above to represent the new ER-diagram.
Hope this will help you now.
I have the following tables :
#Entity
#Table(name = "CUSTOMER")
public class Customers implements Serializable {
private static final long serialVersionUID = -5419345600310440297L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "cust")
#SequenceGenerator(name = "cust", sequenceName = "cust_ID_SEQ")
#Column(name = "CUSTOMER_ID")
private Long id;
#Column(name = "NAME")
private String name;
#OneToMany(mappedBy = "customer", cascade = CascadeType.PERSIST)
private Set<CustomerDeal> customerDeals;
//getters and setters goes here ....
}
#Entity
#Table(name = "DEALS")
public class Deals implements Serializable {
private static final long serialVersionUID = -7197428343863081750L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "deals_seq")
#SequenceGenerator(name = "deals_seq", sequenceName = "DEALS_SEQ")
#Column(name = "DEAL_ID")
private Long dealId;
#Column(name = "DEAL_NAME")
private String dealColName;
//getters setters
}
#Entity
#Table(name = "CUSTOMER_DEALS")
public class CustomerDeals implements Serializable {
private static final long serialVersionUID = -4249326793843278525L;
#EmbeddedId
private CustomerDealId customerDealId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "CUSTOMER_ID", insertable = false, updatable = false)
private Customers customers;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "DEAL_ID", insertable = false, updatable = false)
private Deals deals;
//getters setters
}
#Embeddable
public class CustomerDealId implements Serializable {
private static final long serialVersionUID = 9086627167426987610L;
#Column(name = "DEAL_ID")
private Long dealId;
#Column(name = "CUSTOMER_ID")
private Long customerId;
}
however when I try to create a new customer
Customer cust - new Customer ()
cust.setName("Foo")
CustomerDeals custDeals = new CustomerDeals()
Set<CustomerDeal> custDealsSet = new HashSet<CustomerDeal>
CustomerDeal custDealsSet1 = new CustomerDeal()
CustomerDeal custDealsSet2 = new CustomerDeal()
custDealsSet1.setDeals(dealsRepository.findOne(1))//getting existing deal
custDealsSet1.customers(cust)
custDealsSet2.setDeals(dealsRepository.findOne(2))//getting existing deal
custDealsSet2.customers(cust)
custDealsSet.add(custDealsSet1)
custDealsSet.add(custDealsSet2)
cust.setCustomerDeals(custDealsSet)
customerRepository.saveAndFlush(cust)
customerRepository.saveAndFlush(cust)
I am getting
org.hibernate.id.IdentifierGenerationException: null id generated
for:class CustomerDeal
This is not duplication of this question
Your code that throws exception does not make sense so I guess it is not real code.
CustomerDeal has composite key, so you would not be able to retrieve it with dealsRepository.findOne(1), which means that you probably were retrieveing Deal not CustomerDeal but then the part would never compile:
Set<CustomerDeal> custDealsSet = new HashSet<CustomerDeal>();
custDealsSet.add(dealsRepository.findOne(1))
So, apart from that, I guess you were retrieving the existing deals. And you made a new customer. As the key of CustomerDeal depeneds on both customer and deal, both custumer and deal have to be set before persisting it which you probably forgot to do (and you got your exception). So it should look like:
Customer cust - new Customer ();
cust.setName("Foo");
CustomerDeals custDeal = new CustomerDeals();
custDeal.setCustomer(cust);
custDeal.setDeal(dealsRepository.findOne(1));
cust.getCustomerDeals().add(custDeal);
custDeal = new CustomerDeals();
custDeal.setCustomer(cust);
custDeal.setDeal(dealsRepository.findOne(2));
cust.getCustomerDeals().add(custDeal);
customerRepository.saveAndFlush(cust);
Now you are probably still in trouble. If you override the equals and hash on CustomerDeal so they are ID based (which typical code generator for entities does), both new CustomerDeals instances have them as null, so when you add them to the set the second one will override the first inserted (as null ids will be equals).
You also need to inform JPA that the ID will come from the relations.
In your CustomerDea you need to add #MapsId annotation (on both joins), like:
#MapsId("customerId")
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "CUSTOMER_ID", insertable = false, updatable = false)
private Customers customers;
Finally, unless your CustomerDeal table contains additional apart from CUSTOMER_ID and Deal_ID, then, it is a simple joint table and should not be mapped at all. That way you will save yourself a lot of trouble.
The reason why you got the error mentioned above is due to a mapping issue(I cant figure out what exactly is wrong though). As a completely different approach, I have modified your mappings. I have tested this and it is working fine. The advantage for you with this mapping is that it makes the CustomerDeals class redundant. Please note that I have removed the sequences as I am using MySQL.
#Entity
#Table(name = "CUSTOMERS")
public class Customer implements Serializable {
private static final long serialVersionUID = -5419345600310440297L;
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "CUSTOMER_ID")
private Long id;
#Column(name = "NAME")
private String name;
#ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinTable(
name="CUSTOMER_DEALS",
joinColumns = #JoinColumn( name="CUSTOMER_ID"),
inverseJoinColumns = #JoinColumn( name="DEAL_ID")
)
private Set<Deals> deals = new HashSet<Deals>();
//Setters and Getters to follow
}
The Deals Class will be
#Entity
#Table(name = "DEALS")
public class Deals implements Serializable {
private static final long serialVersionUID = -7197428343863081750L;
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "DEAL_ID")
private Long dealId;
#Column(name = "DEAL_NAME")
private String dealColName;
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "deals")
private Set<Customer> customers = new HashSet<Customer>(0);
//Setters and Getters here
}
Finally the main method which does the insert.
Customer customer = new Customer();
customer.setName("NewCust2");
Deals deals = new Deals();
deals.setDealColName("Deal2");
customer.getDeals().add(deals);
customerRepository.save(customer);
I would like to apologize in advanced if this is a duplicate, however, I've been looking around for the last 2 days and have not found anything that solves my problem.
I have created a web service to which I would like to POST a JSON object. My issue is the following:
Let's say I have three objects.
ObjectA:{
"name":"",
"address":"",
"id":""
}
ObjectB:{
"id:"",
"name":"",
"objectA":{ [ObjectA]}
}
ObjectC:{
"id:"",
"name":"",
"objectA":{},
"objectB":{}
}
As you can see,ObjectC references ObjectA and ObjectB, which also references ObjectA. When inserting a new ObjectC, ObjectC.objectA should be the same as ObjectC.objectB.objectA.
The POST is consumed by the following method:
#POST
#Override
#Consumes({"application/xml", "application/json"})
public void create(ObjectC entity) {
super.create(entity);
}
The classes look like this: (plus the getters and setters)
#Entity
#Table(name = "object_a")
public class ObjectA{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#NotNull
#Column(name = "u_name")
private String uName;
#Column(name = "address")
private String address;
}
#Entity
#Table(name = "object_b")
public class ObjectB{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#JoinColumn(name = "object_a", referencedColumnName = "id")
#ManyToOne(optional = false, cascade = CascadeType.PERSIST)
#Valid
private ObjectA objectA;
}
#Entity
#Table(name = "object_c")
public class ObjectC{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#JoinColumn(name = "object_a", referencedColumnName = "id")
#ManyToOne(optional = false, cascade = CascadeType.PERSIST)
#Valid
private ObjectA objectA;
#JoinColumn(name = "object_b", referencedColumnName = "id")
#ManyToOne(optional = false, cascade = CascadeType.PERSIST)
#Valid
private ObjectB objectB;
}
Note: I am able to POST an ObjectA, and an ObjectB without any problem.
PROBLEM
The problem is that ObjectC.objectA and ObjectC.objectB.objectA are being inserted (or attempted to) as different values, which in throws an exception because ObjectA.uName is unique. If this wasn't the case, two new ObjectA's would have been created.
I was able to solve this problem in Hibernate, by doing something like the following:
ObjectA a = new ObjectA();
ObjectB b = new ObjectB();
ObjectC c = new ObjectC();
b.setObjectA(a);
c.setObjectA(a);
c.setObjectB(b);
session.beginTransaction();
session.save(a);
session.save(b);
session.save(c);
session.getTransaction().commit();
How can I go about this?
For anyone out there who might come across this issue, I have found the solution.
I did it in a very similar way to what I did in hibernate (see above).
I edited the create method I posted above, to this:
#POST
#Override
#Consumes({"application/xml", "application/json"})
public void create(ObjectC entity) {
if(entity.getObjectA().getId()==null){//If objectA doesn't have an ID, it must be new
entity.getObjectB().setObjectA(entity.getObjectA());
em.persist(entity.getObjectA());
em.persist(entity.getObjectB());
em.persist(entity);
}else{//Otherwise, everything may be inserted at once.
super.create(entity);
}
}
By the way, the super.create(entity) method contains the following:
public void create(T entity){
getEntityManager().persist(entity);
}
I need a link between two entities, so I use a one-to-one
#Entity
#Table(name = "T_USER")
public class User implements Serializable {
#Id
#Column(name = "user_id")
private int userId;
#Column(name = "login")
private String login;
#OneToOne(optional = true)
#JoinColumn(name="login", referencedColumnName="person_id", nullable = true, insertable = false, updatable = false)
private Person person;
}
#Entity
#Table(name = "T_PERSON")
public class Person implements Serializable {
#Id
#Column(name = "person_id")
private String personId;
#Column(name = "pin")
private String pin;
}
If there is no item for a particulary PERSON in table T_USER, user.getPerson throw a exception:
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [packagename.com.entity.Person#scabriou]
But If I have reference between the 2 tables in the db, the getter works!
I can't say if this the best solution but you could use the #NotFound annotation. E.g.
#NotFound(action = NotFoundAction.IGNORE)
private Person person;
I believe person will remain null and the exception will not be thrown.