Many-to-many relationship, update referenced Set (update join table) throw NonUniqueObjectException - sql

I am using hibernate. I have a Many-to-Many relationship between Student and Group classes. There is a join table named student_group which has student_id and group_id columns.
Student class:
#Entity
#Table(name = "student")
public class Student {
private int student_id; //primary key
#ManyToMany(mappedBy = "students")
private Set<Group> groups = new HashSet<Group>();
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="student_group",
joinColumns={#JoinColumn(name="student_id")},
inverseJoinColumns={#JoinColumn(name="group_id")})
public Set<Group> getGroups() {
return groups;
}
public void setGroups(Set<Group> groups) {
this.groups = groups;
}
//Setter & Getter for student_id
...
}
Group class:
#Entity
#Table(name = "group")
public class Group {
private int group_id;//primary key
private Set<Student> students = new HashSet<Student>();
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="student_group",
joinColumns={#JoinColumn(name="group_id")},
inverseJoinColumns={#JoinColumn(name="student_id")})
public Set<Student> getStudents() {
return students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
//Getter & Setting for group_id
...
}
In my main program I have a function which 1st find a student from database, then, add a group to the student and finally try to save the updated student to database:
public void addGroupToStudent(int studentId, Group newGroup){
//open a session & query the database to get a student by id
Session session = sessionFactory.openSession();
Student student = loadStudentById(session, id);
//Get above student's groups
Set<Group> groups = student.getGroups();
//Add the new group
groups.add(newGroup);
//SaveAndUpdate the student in database
Transaction transaction = session.beginTransaction();
try {
session.saveOrUpdate(student);
transaction.commit();
} catch (final HibernateException e) {...}
}
(The newGroup parameter in above function is retrieved from database with another session & query.)
But the above function throw the following exception:
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.my.db.Group#5]
at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:697)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:296)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:241)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:109)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
Where am I wrong? How to get rid of this exception? Generally I just want to update a student's groups in database, which in turns should update the join table.

Related

While using Spring MVC I am trying to insert a table data to another one using #Controller but don't know how

I am trying to transfer by insert from a table to another table in #Controller
I will explain here better,
So I have an Employee table and i want to transfer first name . last name and function , to the Monday table, but I don't really understand how I can do it .
here are some code i did PS. the language is Romanian so Luni = Monday the rest is is obvious.
I have all the CRUD implemented
WeekdayDAO
public class WeekdayDAO {
public void insertLuni(Luni luni) throws SQLException{
Connection conn = DBHelper.getConnection();
String insertString=" INSERT INTO luni (nume, prenume, funcite) SELECT nume, prenume,funtie FROM employee WHERE id=?" ;
PreparedStatement stmt = conn.prepareStatement(insertString);
stmt.setString(1, luni.getNume() );
stmt.setString(2, luni.getPrenume());
stmt.setString(3, luni.getFunctie());
stmt.executeUpdate();
DBHelper.closeConnection(conn);
}
Calendar controller
Explanation : so I need to create a link when i chose an employee by id to just add all the data I requested in Monday, because after that I want to show it in a table like all the Employees I added in that day.
#Controller
public class WeekController {
#RequestMapping(value = "detalii/{employeeId}")
public ModelAndView getEmployeeDetails(#PathVariable String employeeId) throws SQLException {
EmployeeDAO edao = new EmployeeDAO();
Employee employee = edao.getEmployeeById(employeeId);
edao.getEmployeeById(employeeId);
ModelMap model = new ModelMap();
model.put("employee", employee);
return new ModelAndView("employee/detalii", "model", model);
}
#RequestMapping(value = "detalii/{employeeId}"/addEmployeeDay.htm", method = RequestMethod.POST)
public ModelAndView addEmployeeDay(#PathVariable String employeeId, Model model) throws SQLException {
EmployeeDAO edao = new EmployeeDAO();
Employee employee = edao.getEmployeeById(employeeId);
edao.getEmployeeById(employeeId);
WeekdayDAO wdao = new WeekdayDAO();
Luni luni= wdao.insertLuni(luni);
return new ModelAndView("employee/edit", "model", model);
}
}
Other example is trying to change some stuff from the edit Employee but it doesn't work .
#RequestMapping(value = "editEmployee/{employeeId}")
public ModelAndView displayEditForm(#PathVariable String employeeId, Model model) throws SQLException {
EmployeeDAO edao = new EmployeeDAO();
Employee e = edao.getEmployeeById(employeeId);
model.addAttribute("employeeForm", e);
return new ModelAndView("employee/edit", "model", model);
}

Error in apex test logs: common.apex.runtime.impl.ExecutionException: List has no rows for assignment to SObject"

Here is what i tried. I think the problem is somewhere in the test class when i create the apex test controller and i want to link the record of the controller to the solution that i created in the test class.
This is the error that i found in logs:
"common.apex.runtime.impl.ExecutionException: List has no rows for assignment to SObject"|0x4888dd3c
apex class:
public with sharing class SolutionWrapper {
public ApexPages.StandardSetController controller;
public Opportunity opp{get; set;}
public Solutions__c oppId{get; set;}
public Solutions__c sol{get; set;}
public Solutions__c solution{get; set;}
public Account acc{get; set;}
public SolutionWrapper(ApexPages.StandardSetController controller) {
try{
solution = new Solutions__c();
solution = (Solutions__c)controller.getRecord();
if(solution.Id != null){
oppId = [SELECT id, Solutions__c.Opportunity__c
FROM Solutions__c
WHERE id =: solution.Id
LIMIT 1];
opp = [Select id,Name, AccountId, CurrencyIsoCode from
Opportunity where id =: oppId.Opportunity__c LIMIT:1];
}
if(opp.id !=null){
sol = [SELECT id,Name, Mail_Merge_Id__c,Solution_Country__c, Solutions__c.Opportunity__c
FROM Solutions__c
WHERE Solutions__c.Opportunity__c =: opp.id
LIMIT 1];
acc = [Select id,Name,Country__c from
Account where id=:opp.AccountId LIMIT: 1];
}
}
catch(Exception e){
ApexPages.addMessage(new ApexPages.message(ApexPages.Severity.ERROR,e.getMessage()));
}
}
}
Here is my test class
apex test class:
#isTest
public class SolutionWrapperTest {
static testMethod void testMethodOpp(){
Account acc = new Account(Name='test', Country__c='test');
insert acc;
Opportunity opp = new Opportunity(Name='test', AccountId=acc.id, CurrencyIsoCode='GBP', StageName = 'Good',
CloseDate = date.today());
insert opp;
Solutions__c sol = new Solutions__c( Opportunity__c= opp.id, CurrencyIsoCode='GBP');
insert sol;
List<Solutions__c> listSol = new List<Solutions__c>();
listSol.add(sol);
PageReference pageRef = Page.NewVisualForcePage;
Test.setCurrentPage(pageRef);
Test.startTest();
ApexPages.StandardSetController stdController = new ApexPages.StandardSetController(listSol);
SolutionWrapper testSolution = new SolutionWrapper(stdController);
Test.stopTest();
}
}
Solution id is different from the one that i inserted in the test class. I inserted a dummy value (eq 'test') on a field like Currency. After that i selected from the db based on Currency instead of id.

Spring Data Rest Left Outer Join non-Entity POJO Null Entity Error

How do i accomplish getting the this interface method to work? i am using a MySQL DB if that matters...
public interface PersonRoleRepository extends CrudRepository<PersonRole,Long>{
//This causes null entity error from hibernate even though the SQL works outside hibernate
#Query(value="select * from Role r left outer join Person_Role pr on r.id = pr.role_id and pr.person_id = ? order by pr.expires_date desc", nativeQuery = true)
List<PersonRoleDto> getAllRolesAndPersonsStatusWithEachRole(int personId);
}
Here is the SQL query that returns what i want in SQL Workbench...
Select r.*, pr.*
from
Role r
left outer join person_role pr on r.id = pr.role_id and pr.person_id = ?
order by pr.expires_date desc;
Important MySQL database structure...
Table: Role
role_id bigint
name varchar
description varchar
...
Table: Person
person_id bigint
first_name varchar
last_name varchar
...
Table Person_Role_Link
person_role_id bigint
role_id bigint
person_id bigint
...
alter table person_Role_Link add constraint l1 foreign key (person_id) references Person(person_id)
alter table person_Role_Link add constraint l2 foreign key (role_id) references Role(role_id)
Here is the entity info...
#Entity
#Table(name="Role")
#EntityListeners(AuditEntityListener.class)
public class Role extends AbstractAuditEntity {
private static final long serialVersionUID= 44543543543543454543543L;
#id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="role_id")
private long id;
#NotNull
private String fullName
...
#OneToMany(mappedBy="role",cascade=CascadeType.ALL)
private Set<PersonRole> personRoles = new HashSet<>();
...
}
#Entity
#Table(name="Person_Role_Link")
#EntityListeners(AuditEntityListener.class)
class PersonRole extends AbstractAuditEntry{
private static final long serialVersion = 54324243242432423L;
#id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="person_role_id")
private long id;
#ManyToOne
#JoinColumn(name="person_id")
private Person person;
#ManyToOne
#JoinColumn(name="role_id")
private Role role;
...
}
#Entity
#Table(name="Person")
#EntityListeners(AuditEntityListener.class)
public class Person extends AbstractAuditEntity{
private ... serialVersionUID...;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="person_id")
private Long id;
...
#OneToMany(mappedBy="person", cascade=CascadeType.ALL)
private Set<PersonRole> personRoles = new HashSet<>();
...
}
Just for now i made a simple interface...
public interface PersonRoleDto {
String getFullName();
String getDescription();
//i want more attributes but for now i will just see if it works with the basics
}
Here is the latest HQL i tried...
public interface PersonRoleRepository extends CrudRepository<PersonRole,Long>{
//PersistentEntity must not be null
#Query("select r.fullName as fullName, r.description as description from Role r left join r.personRoles pr where pr.person = :person")
List<PersonRoleDto> findAllRolesWithPersonsStatus(#Param("person") Person person);
...
}
Whether I use HQL or native SQL i get a null entity error from hibernate. Also, the SQL generated from the HQL works without error but i still get a null entity error in code and, second, the SQL generated from HQL is slightly off which makes the results off. That's why i was trying so hard to get the native SQL to work.
The relationship is used to figure out how many people are in a role and at other times what roles a person has. This is a circular relationship, i'd say. I work on an Intranet so i had to hand type everything. If there are any problems seen in my code other than with the stated native query as stated then it is most likely because i had to hand type everything and not because the code is buggy. Everything else works so far but this one thing.
All help is appreciated.
UPDATE!!!!
I think this is the answer to my problem but when i try it i still get the error: PersistentEntity must not be null!
Here is how i tried to set it up...
//Added this to the top of PersonRole entity
#SqlResultSetMapping(
name="allRolesAndPersonsStatusWithEachRole"
classes={
#ConstructorResult(
targetClass=PersonRoleStatus.class,
columns={
#ColumnResult(name="full_name"),
#ColumnResult(name="description"),
...
}
)
}
)
#NamedNativeQuery(name="PersonRole.getAllRolesAndPersonsStatusWithEachRole",
query="Select r.*, pr.* from Role r Left Join person_role_link on r.role_id = pr.role_id and pr.person_id = :id", resultSetMapping="allRolesAndPersonsStatusWithEachRole")
Created my DTO like this...
public class RolePersonStatus {
private String fullName;
private String description;
private String ...
public RolePersonStatus(String fullName, String description, ...){
this.fullName = fullName;
this.description = description;
...
}
}
In my repository i just have:
//No annotation because it stated that the name of the method just needed to match the native query name?!?!?
List<RolePersonStatus> findAllRolesWithPersonStatus(#Param("id" Long id);
What am i missing???????
Try this way:
Entities
#Entity
#Table(name = "parents")
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany
private List<Child> children = new ArrayList<>();
//...
}
#Entity
#Table(name = "children")
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne(optional = false)
private Reference reference;
#OneToMany
private final List<Toy> toys = new ArrayList<>();
//...
}
#Entity
#Table(name = "references")
public class Reference {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String description;
//...
}
#Entity
#Table(name = "toys")
public class Toy {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
//...
}
DTO
public interface DemoDto {
String getParentName();
String getChildName();
String getToyName();
String getDescription();
}
Repository
public interface ParentRepo extends JpaRepository<Parent, Long> {
#Query("select " +
"p.name as parentName, " +
"c.name as childName, " +
"t.name as toyName, " +
"r.description as description " +
"from " +
"Parent p " +
"join p.children c " +
"join c.reference r " +
"join c.toys t " +
"where c.id = ?1 " +
"order by r.description desc")
List<DemoDto> getDto(Long childId);
}
Usage
#RunWith(SpringRunner.class)
#SpringBootTest
public class ParentRepoTest {
#Autowired
private ParentRepo parentRepo;
#Test
public void getDto() throws Exception {
List<DemoDto> dtos = parentRepo.getDto(3L);
dtos.forEach(System.out::println);
}
}
Result
{parentName=parent2, toyName=Toy7, childName=child3, description=Description1}
{parentName=parent2, toyName=Toy8, childName=child3, description=Description1}
{parentName=parent2, toyName=Toy9, childName=child3, description=Description1}
More info is here.
Working example.

#ManyToMany mapping in JPA throws Exception [EclipseLink-6076]

I have developed simple JPA2.1 application with Oracle 11g database using Eclipse 2.5.2. Following is my scenario - there are two entities with #ManyToMany relation
Club
People
Club.java
#Entity(name = "CLUB")
public class Club {
#Id
private int c_id;
private String c_name;
#ManyToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE},
fetch=FetchType.EAGER)
#JoinTable(name = "CLUB_PEOPLE",
joinColumns=#JoinColumn(name="c_id", referencedColumnName="c_id"),
inverseJoinColumns=#JoinColumn(name="p_id"))
private Set<People> people=new HashSet<People>();
public int getC_id() {
return c_id;
}
public void setC_id(int c_id) {
this.c_id = c_id;
}
public String getC_name() {
return c_name;
}
public void setC_name(String c_name) {
this.c_name = c_name;
}
public Set<People> getPeople() {
return people;
}
public void setPeople(Set<People> people) {
this.people = people;
}
public void addPeople(People p){
getPeople().add(p);
}
}
People.Java
#Entity(name="people")
public class People{
#Id
private int p_id;
private String name ;
#ManyToMany(mappedBy="people")
private Set<Club> club;
public int getP_id() {
return p_id;
}
public void setP_id(int p_id) {
this.p_id = p_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Client.java
public class Client1 {
public static void main(String[] args) {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("JPA-Query");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Club> cq = cb.createQuery(Club.class).distinct(true);
Root<Club> root = cq.from(Club.class);
final List<Predicate> requiredCriteria = new ArrayList<Predicate>();
final Join people = root.join("people", JoinType.LEFT);
requiredCriteria.add(cb.or(cb.isNull(people)));
requiredCriteria.add(cb.isNull(people.get("name")));
requiredCriteria.add(cb.equal(people.get("name"), "people1"));
TypedQuery<Club> q = em.createQuery(
cq.where(cb.or(requiredCriteria
.toArray(new Predicate[requiredCriteria.size()])
))
);
List<Club> allemps = q.getResultList();
em.close();
emf.close();
}
}
Tables -
CREATE TABLE Club(
c_id number NOT NULL,
c_name VARCHAR2(20)
)
ALTER TABLE Club ADD CONSTRAINT CLUGGRPPK PRIMARY KEY (c_id);
CREATE TABLE People(
p_id number NOT NULL,
name VARCHAR2(20)
)
ALTER TABLE People ADD CONSTRAINT PEOPLEGRPPK PRIMARY KEY (p_id);
CREATE TABLE CLUB_PEOPLE
(
C_ID number,
P_ID number,
CONSTRAINT CLUB_PEOPLEPK PRIMARY KEY (C_ID, P_ID),
CONSTRAINT GROUP_FK FOREIGN KEY("C_ID")
REFERENCES "CLUB"("C_ID") ON DELETE CASCADE,
CONSTRAINT PEOPLE_FK FOREIGN KEY("P_ID")
REFERENCES "PEOPLE"("P_ID") ON DELETE CASCADE
)
When I run my Client.java it throws following exception
Exception Description: Object comparisons can only be used with OneToOneMappings. Other mapping comparisons must be done through query keys or direct attribute level comparisons.
Mapping: [org.eclipse.persistence.mappings.ManyToManyMapping[people]]
Expression: [
Query Key people
Base ManyToMany.Club]
Query: ReadAllQuery(referenceClass=Club )
javax.persistence.PersistenceException: Exception [EclipseLink-6076] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.QueryException
Exception Description: Object comparisons can only be used with OneToOneMappings. Other mapping comparisons must be done through query keys or direct attribute level comparisons.
Mapping: [org.eclipse.persistence.mappings.ManyToManyMapping[people]]
Expression: [
Query Key people
Base ManyToMany.Club]
Query: ReadAllQuery(referenceClass=Club )
at org.eclipse.persistence.internal.jpa.QueryImpl.getResultList(QueryImpl.java:480)
at ManyToMany.Client.main(Client.java:75)
Caused by: Exception [EclipseLink-6076] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.QueryException
Exception Description: Object comparisons can only be used with OneToOneMappings. Other mapping comparisons must be done through query keys or direct attribute level comparisons.
Mapping: [org.eclipse.persistence.mappings.ManyToManyMapping[people]]
Expression: [
Query Key people
Base ManyToMany.Club]
Query: ReadAllQuery(referenceClass=Club )
at org.eclipse.persistence.exceptions.QueryException.unsupportedMappingForObjectComparison(QueryException.java:1170)
Please help me to solve above issue. Thanks in advance.

Why my table is not insertable?

Well, I've got two tables: SaveBatch and SaveBatchLocal. They have equal fields. First table is insertable in Hibernate and second is not. This is my code:
#Override
#Transactional(readOnly = false)
public void add(LinkedList<SmsEntity> smsEntityList) {
try {
Session session = null;
session = this.sessionFactory.getCurrentSession();
Iterator<SmsEntity> iterator = smsEntityList.iterator();
while (iterator.hasNext()) {
session.save(iterator.next());
}
} catch (Exception e) {
e.printStackTrace();
}
}
The only thing I change is table name in entity class:
#Entity
#Table(name = "smsc.SaveBatch")
public class SmsEntity {
#Id
#Column(name = "id")
private int id;
#Column(name = "nextid")
private Integer nextId;
...
If #Table(name = "smsc.SaveBatch")
everything is fine, if
#Table(name = "smsc.SaveBatchLocal")
no new rows. What is wrong? Also, this is my Oracle SQL Developer pic:
They have got different icons with unknown meanings.
SaveBatchLocal is a global temporary table, which means that its contents are only visible to the connection that inserted them, and are automatically deleted when that session ends.
See http://www.oracle-base.com/articles/misc/temporary-tables.php