I'm trying to run a UT but is failing at the #Before method. This is the error:
Caused by: org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "UK_PBNJJ4MCIQ51S0SJV9U3J2WQ4_INDEX_5 ON PUBLIC.XACTIVITYCONTENTTYPE(CONTENTTYPE_ID) VALUES (19, 1)"; SQL statement:
insert into XACTIVITYCONTENTTYPE (ACTIVITY_ID, CONTENTTYPE_ID) values (?, ?) [23505-197]
I have an array of object(ActivityEntity) which I'm initializing and persisting in a H2 DB:
for (int i = 0; i < activities.length; i++) {
Date createdDate = new Date();
ActivityEntity activity = new ActivityEntity();
activity.setType(ActivityType.valueOf(properties.getType()));
activity.setLabel(ActivityLabel.valueOf(properties.getLabel()));
activity.setStatus(Status.valueOf(properties.getStatus()));
activity.setDeliveryType(DeliveryType.valueOf(properties.getDeliveryType()));
activity.setSubject(em.find(SubjectEntity.class, subjectId));
activity.setFontSize(FontSize.valueOf(properties.getFontSize()));
activity.setEstimatedTime(ESTIMATED_TIME);
activity.setPlannedTime(properties.getPlannedTime());
activity.setInteractivityType(InteractivityType.valueOf(properties.getInteractivityType()));
activity.setAudience(Audience.valueOf(properties.getAudience()));
activity.setPurpose(Purpose.valueOf(properties.getPurpose()));
activity.setAcademicLevel(AcademicLevel.valueOf(properties.getAcademicLevel()));
activity.setEnvironment(Environment.valueOf(properties.getEnvironment()));
activity.setInstructionMethod(InstructionMethod.valueOf(properties.getInstructionMethod()));
activity.setCreatedBy(CREATED_BY);
activity.setCreatedDate(createdDate);
activity.setModifiedBy(CREATED_BY);
activity.setModifiedDate(createdDate);
activity.setDeprecated(properties.isDeprecated());
activity.setTemplate(properties.isTemplate());
activity.setCurriculumProvider(CurriculumProvider.valueOf(properties.getCurriculumProvider()));
activity.setShowLessonNavigator(properties.isShowLessonNavigator());
activity.setShowHeader(properties.isShowHeader());
activity.setDisplayModuleType(properties.isDisplayModuleType());
activity.setDisplayLabelType(properties.isDisplayLabelType());
activity.setShowFooter(properties.isShowFooter());
activity.setShowPagination(properties.isShowPagination());
activity.setDisplayProgressBar(properties.isDisplayProgressBar());
activity.setDisplayResources(properties.isDisplayResources());
activity.setLanguage(language);
activity.setPrimaryStatus(PrimaryStatus.valueOf(properties.getPrimaryStatus()));
activity.setIntendedDeliveryType(IntendedDeliveryType.valueOf(properties.getIntendedDeliveryType()));
activity.setNextGen(properties.isNextGen());
activity.setExcludeFromSearch(properties.isExcludeFromSearch());
activity.setExcludeFromRecommender(properties.isExcludeFromRecommender());
activity.setTeacherCreated(properties.isTeacherCreated());
activity.setTitle(TITLE + (i + 1), language);
activity.getGrades().addAll(grades);
activity.getStudentGroupings().add(new StudentGroupingEntity(properties.getStudentGroupingId()));
activity.getPedagogicalIntents().add(new PedagogicalIntentEntity(properties.getPedagogicalIntentId()));
activity.getLearnerTypes().add(new LearnerTypeEntity(properties.getLearnerTypeId()));
activity.getContentTypes().add(new ContentTypeEntity(properties.getContentTypeId()));
activities[i] = em.persist(activity);
}
em.flush();
The last set it's the property related to the error. The properties have a value of 19 for the ContentTypeId. Now, this is part of the Activity entity class:
#Entity
#Table(name = "ACTIVITY")
public class ActivityEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SQ_ACTIVITY_ACTIVITY_ID")
#SequenceGenerator(name = "SQ_ACTIVITY_ACTIVITY_ID", sequenceName = "SQ_ACTIVITY_ACTIVITY_ID", allocationSize = 1)
#Column(name = "ACTIVITY_ID")
private Integer id;
//MORE FIELDS LEFT OUT FOR CLARITY
#ManyToMany
#JoinTable(name = "XACTIVITYCONTENTTYPE", joinColumns = { #JoinColumn(name = "ACTIVITY_ID", referencedColumnName = "ACTIVITY_ID") }, inverseJoinColumns = { #JoinColumn(name = "CONTENTTYPE_ID", referencedColumnName="ID") } )
private List<ContentTypeEntity> contentTypes = new ArrayList<>();
}
And here's the ContentTypeEntity class:
#Entity
#Table(name = "CONTENTTYPE")
public class ContentTypeEntity {
#Id
#Column(name = "ID")
private int id;
#Column(name = "NAME")
private String name;
#Column(name = "SEQ_NUM")
private int seqNum;
public ContentTypeEntity() {
}
public ContentTypeEntity(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSeqNum() {
return seqNum;
}
public void setSeqNum(int seqNum) {
this.seqNum = seqNum;
}
}
If I debug, the ids for ActivityEntity is being generated correctly for each of the 3 objects that i'm putting in the array (ids=[1,2,3]). So i don't understand why the second insert is using the id=1, which is what the exception is implying. If I put one ActivityEntity in the array, everything works correctly.
You haven't pasted in all the code and you cannot assign the return type of em.persist, but i think the issue is probably related to creating multiple instances of contenttype with id 19.
Assuming content type with id 19 is already persisted and you are attempting to just create references to it rather than persist it as you have no cascade in your ManyToMany you can do something like this as this sample test code shows. The transaction are there as I don't know your tx boundaries in your code and just for the purpose of saving the contenttype separately
#Test
public void saveActivities() {
// Tx1 - Persist content type
EntityTransaction tx1 = em.getTransaction();
tx1.begin();
ContentTypeEntity contentType = new ContentTypeEntity(19);
em.persist(new ContentTypeEntity(19));
tx1.commit();
em.detach(contentType);
// Tx2 - Persist activities using a reference to content type
EntityTransaction tx2 = em.getTransaction();
tx2.begin();
for (int i = 0; i < 3; i++) {
ActivityEntity activity = new ActivityEntity();
activity.getContentTypes().add(em.getReference(ContentTypeEntity.class, 19));
em.persist(activity);
}
tx2.commit();
// assertions
}
Related
I have two entities and a service. Without #Transactional everything worked fine (except rollback). Now I added a #Transactional to the service method to make it an transaction and rollback automatically on errors. But now all tests using this method fail with javax.persistence.EntityNotFoundException: Unable to find org.kitodo.mediaserver.core.db.entities.Work with id xyz (xyz is the ID of my Work item).
Then I tried to add cascade = {CascadeType.PERSIST, CascadeType.MERGE} to the work field of ActionData entity. Than I get another exception on the same position as before: org.h2.jdbc.JdbcSQLException: Concurrent update in table "WORK": another transaction has updated or deleted the same row [90131-196]
I assume for some reason it tries to use two transitions at the same time.
What's the reason and how can I make this work?
The entities
#Entity
public class Work {
private String id;
private String title;
private String path;
private String hostId;
private Instant indexTime;
private Set<Collection> collections;
private String allowedNetwork = "global";
protected Work() {}
public Work(String id, String title) {
this.id = id;
this.title = title;
}
#Id
public String getId() {
return id;
}
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "work_collection",
joinColumns = #JoinColumn(name = "work_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "collection_name", referencedColumnName = "name"))
public Set<Collection> getCollections() {
return collections;
}
// getters/setters
}
#Entity
public class ActionData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ElementCollection
#CollectionTable(name = "action_parameter")
private Map<String, String> parameter = new HashMap<>();
#ManyToOne
#JoinColumn(name = "work_id", referencedColumnName = "id", nullable = false)
private Work work;
private String actionName;
private Instant requestTime;
private Instant startTime;
private Instant endTime;
private ActionData() {}
public ActionData(Work work, String actionName, Map<String, String> parameter) {
this.work = work;
this.parameter = parameter;
this.actionName = actionName;
}
// getters/setters
}
The Service method
#Service
public class ActionService {
#Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public Object performRequested(ActionData actionData) throws Exception {
// some checks
actionData.setStartTime(Instant.now());
// !!! javax.persistence.EntityNotFoundException: Unable to find org.kitodo.mediaserver.core.db.entities.Work with id xyz
actionRepository.save(actionData);
IAction actionInstance = getActionInstance(actionData.getActionName());
Object result;
result = actionInstance.perform(actionData.getWork(), actionData.getParameter());
actionData.setEndTime(Instant.now());
actionRepository.save(actionData);
return result;
}
}
The test
#Test
public void performRequestedAction() throws Exception {
// given
init();
work1 = entityManager.persist(work1);
actionData1 = new ActionData(work1, "mockAction", parameter1);
actionData1.setRequestTime(Instant.now());
actionData1 = entityManager.persist(actionData1);
entityManager.flush();
// when
Object action = actionService.performRequested(actionData1);
// then
assertThat(action).isNotNull();
assertThat(action).isInstanceOf(String.class);
assertThat(action).isEqualTo("performed");
assertThat(actionData1.getStartTime()).isBetween(Instant.now().minusSeconds(2), Instant.now());
assertThat(actionData1.getEndTime()).isBetween(Instant.now().minusSeconds(2), Instant.now());
}
I suspect, your Unittest is not working in autocommit mode.
The problem might be, that you don't commit the inserting-transaction in your testing function.
Therefore the saved data can not be seen by the called method actionService.performRequested() which starts a completely new transaction. This transaction will not be allowed to see any dirty data.
So either make sure that the data is saved either by setting autocommit-mode or committing the transaction which persists actionData1 in performRequestedAction.
I created two tables in Oracle SQL Developer editor whos realtion is Many-To-Many, and I also created their hibernate classes 'TestEmployee' and 'TestProject' as shown below in the code. As the relation between the two classes is
Many-To-Many, however a new table named 'Employee_Project2' was created in Oracle SQL Developer editor to hold te primary keys of the other two tables 'TestEmployee' and 'TestProject'.
Values to 'TestEmployee' and 'TestProject' were inserted through Hibernate as shown belwo in section 'records insertion'.
The problem i facing now is, when I run the follwoing command:
SELECT * from Employee_Project2;
from Oracle SQL Developer Editor, i get an empty table despite it is mentioned in the annotation of the Hibernate class 'TestProject' as follwos:
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "Employee_Project2", joinColumns = #JoinColumn(name = "proj_id"), inverseJoinColumns = #JoinColumn(name = "emp_id"))
private Set<TestEmployee> employeesList;
Please let me know why despite there are records inserted into both 'TestEmployee' and 'TestProject' tables, the table 'Employee_Project2' is empty??
note:
I have not explicitly inserted any records into 'Employee_Project2' neither through Hibernate nor Oracle SQL Developer editor, because I expect the records "primary key" to be inserted automatically through Hibernate as the table 'Employee_Project2' is mentioned in the annotation
TestEmployee:
#Entity #Table(schema = "afk_owner", name = "Test_Employee2")
public class TestEmployee {
#Id
#Column(name = "emp_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequencegen")
#SequenceGenerator(name = "sequencegen", sequenceName = "afk_owner.Test_Employee_seq", allocationSize = 1)
private Long mEmpId;
#Column(name = "emp_name")
private String mEmpName;
#Column(name = "emp_experience")
private int mEmpExperience;
#ManyToMany(cascade = CascadeType.ALL, mappedBy = "employeesList")
private Set<TestProject> mProjectsList;
public Long getmEmpId() {
return mEmpId;
}
public void setmEmpId(Long mEmpId) {
this.mEmpId = mEmpId;
}
public String getmEmpName() {
return mEmpName;
}
public void setmEmpName(String mEmpName) {
this.mEmpName = mEmpName;
}
public int getmEmpExperience() {
return mEmpExperience;
}
public void setmEmpExperience(int mEmpExperience) {
this.mEmpExperience = mEmpExperience;
}
public Set<TestProject> getmProjectsList() {
return mProjectsList;
}
public void setmProjectsList(Set<TestProject> mProjectsList) {
this.mProjectsList = mProjectsList;
}
public TestEmployee(String empName, int empExperience) {
this.mEmpName = empName;
this.mEmpExperience = empExperience;
}
public TestEmployee() {
// TODO Auto-generated constructor stub
}
}
TestProject:
#Entity #Table(schema = "afk_owner", name = "Test_Project2")
public class TestProject {
#Id
#Column(name = "proj_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequencegen")
#SequenceGenerator(name = "sequencegen", sequenceName = "afk_owner.Test_Project_seq", allocationSize = 1)
private Long mProjId;
#Column(name = "proj_name")
private String mProjName;
#Column(name = "proj_desc")
private String mProjDesc;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "Employee_Project2", joinColumns = #JoinColumn(name = "proj_id"), inverseJoinColumns = #JoinColumn(name = "emp_id"))
private Set<TestEmployee> employeesList;
public Long getmProjId() {
return mProjId;
}
public void setmProjId(Long mProjId) {
this.mProjId = mProjId;
}
public String getmProjName() {
return mProjName;
}
public void setmProjName(String mProjName) {
this.mProjName = mProjName;
}
public String getmProjDesc() {
return mProjDesc;
}
public void setmProjDesc(String mProjDesc) {
this.mProjDesc = mProjDesc;
}
public Set<TestEmployee> getEmployeesList() {
return employeesList;
}
public void setEmployeesList(Set<TestEmployee> employeesList) {
this.employeesList = employeesList;
}
public TestProject(String projName, String projDesc) {
this.mProjName = projName;
this.mProjDesc = projDesc;
}
public TestProject() {
// TODO Auto-generated constructor stub
}
}
records insertion
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = session.beginTransaction();
/*empAhmad*/
TestEmployee empAhmad = new TestEmployee();
TestProject projRoadSteepnessEstimation = new TestProject();
TestProject projObjectTrackingUsingLIDAR = new TestProject();
TestProject projSalientRegionDetector = new TestProject();
TestProject projAutonomousNavigationUsingGNSSSensors = new TestProject();
Set<TestProject> empAhmadProjLists = new HashSet<>();
empAhmad.setmEmpName("Ahmad");
empAhmad.setmEmpExperience(9);
projRoadSteepnessEstimation.setmProjName("Road Steepness Est");
projRoadSteepnessEstimation.setmProjDesc("Kalman Filter, Java");
empAhmadProjLists.add(projRoadSteepnessEstimation);
projObjectTrackingUsingLIDAR.setmProjName("Object Tracking LIDAR");
projObjectTrackingUsingLIDAR.setmProjDesc("C++, OpenCV");
empAhmadProjLists.add(projObjectTrackingUsingLIDAR);
projSalientRegionDetector.setmProjName("Salient Region Detector");
projSalientRegionDetector.setmProjDesc("Java, OpenCV");
empAhmadProjLists.add(projSalientRegionDetector);
projAutonomousNavigationUsingGNSSSensors.setmProjName("Autonomous Navigation GNSS");
projAutonomousNavigationUsingGNSSSensors.setmProjDesc("Android, Kalman Filter");
empAhmadProjLists.add(projAutonomousNavigationUsingGNSSSensors);
empAhmad.setmProjectsList(empAhmadProjLists);
/*empAmr*/
TestEmployee empAmr = new TestEmployee();
TestProject projKalmanForOnlineEstimation = new TestProject();
TestProject projNonLinearControlAndFiltering = new TestProject();
TestProject projAppForHydrolicProcess = new TestProject();
Set<TestProject> empAmrProjList = new HashSet<>();
empAmr.setmEmpName("Amr");
empAmr.setmEmpExperience(5);
projKalmanForOnlineEstimation.setmProjName("Kalman For Online Estimation");
projKalmanForOnlineEstimation.setmProjDesc("Kalman Filter, Java, C++");
empAmrProjList.add(projKalmanForOnlineEstimation);
projNonLinearControlAndFiltering.setmProjName("Non-Linear Control And Filtering");
projNonLinearControlAndFiltering.setmProjDesc("C++, wavelet analysis");
empAmrProjList.add(projNonLinearControlAndFiltering);
projAppForHydrolicProcess.setmProjName("App For Hydrolic Process");
projAppForHydrolicProcess.setmProjDesc("Android, OpenCV, C++");
empAmrProjList.add(projAppForHydrolicProcess);
empAmr.setmProjectsList(empAmrProjList);
/*empAli*/
TestEmployee empAli = new TestEmployee();
Set<TestProject> empAliProjList = new HashSet<>();
empAli.setmEmpName("Ali");
empAli.setmEmpExperience(7);
empAliProjList.add(projAutonomousNavigationUsingGNSSSensors);
empAliProjList.add(projObjectTrackingUsingLIDAR);
empAliProjList.add(projKalmanForOnlineEstimation);
empAliProjList.add(projAppForHydrolicProcess);
empAli.setmProjectsList(empAliProjList);
session.persist(empAhmad);
session.persist(empAmr);
session.persist(empAli);
transaction.commit();
Because you never inserted anything in the owning side tof the association: Project.employeesList. As simple as that.
You only populated the inverse side of the association: Employee.mProjectsList, but Hibernate only cares about the owning side.
Im currently working with Hibernate and Java, im trying to persist objects in our database. We have a Group class and a ToDoList class. Group has a one-to-many relationship with ToDoList as show in this ERD.
The relevant code from the Group class:
#Entity
#Table(name = "Group", catalog = "db")
public class Group implements java.io.Serializable{
private int id;
private Set<ToDoList> allToDoLists;
public Group(){
allToDoLists = new HashSet<ToDoList>(0);
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "Group_id", unique = true, nullable = false)
public int getId(){
return this.id;
}
public void setId(int id){
this.id = id;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "group")
public Set<ToDoList> getAllToDoLists() {
return this.allToDoLists;
}
public void addTodoList(ToDoList t){
this.allToDoLists.add(t);
t.setGroup(this);
}
public void setAllToDoLists(Set<ToDoList> allToDoLists) {
this.allToDoLists = allToDoLists;
}
}
The relevant code from the ToDoList class:
#Entity
#Table (name = "Todo_List", catalog = "db")
public class ToDoList {
private int id;
private Group group;
public ToDoList(){}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "Todo_List_id", unique = true, nullable = false)
public int getId(){
return this.id;
}
public void setId(int id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "Group", nullable = false)
public Group getGroup() {
return this.group;
}
public void setGroup(Group group) {
this.group = group;
}
}
The code where the problem occurs:
ToDoList toDoList = new ToDoList("TodoList1","Iets","24-06-2015","24-06-2015");
Group group = new Group("Groep 1","10-11-2011","10-11-2011");
GroupDao groupDao = new GroupDaoImpl();
groupDao.store(group);
toDoList.setGroup(group);
toDoListDao.store(toDoList);
The code in 'groupDao' and 'toDoListDao' for storing are basicly the same:
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
session.save(toDoList);
session.getTransaction().commit();
Problem
When I try to store the ToDoList I get the following error:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Group, Name) values ('2015-06-24', '2015-06-24', 'Iets', 23, 'TodoList1')' at line 1
Needed
It whould be best if I could store Group and ToDoList whould be save also.
Given our coming deadline it whould also suffice if I can store Group and ToDoList on their own
"Group" is an SQL keyword ("group by"). As such it is not the best choice of table name. It looks like this is causing conflicts given that Hibernate is generating SQL not accepted by the DBMS.
If you can't change the table name, you can probably solve that conflict by quoting the name in your entity, like so:
#Table(name = "`Group`", catalog = "db")
or
#Table(name = "\"Group\"", catalog = "db")
EDIT:
And as ug_ informs me, you can also set the following hibernate property in your persistence.xml file to do that by default to all identifiers:
<property name="hibernate.globally_quoted_identifiers" value="true" />
I have never used that myself, but the Javadocs confirm that this is indeed a supported property:
https://docs.jboss.org/hibernate/orm/4.2/javadocs/org/hibernate/cfg/AvailableSettings.html
I am working on a Spring-MVC application in which I am trying to search for List of GroupNotes in database. The mapping in my project is GroupCanvas has one-to-many mapping with GroupSection and GroupSection has one-to-many mapping with GroupNotes. Because of these mappings, I was getting LazyInitializationException. As suggested on SO, I should be converting the objects to a DTO objects for transfer. I checked on net, but couldnt find a suitable way to translate those.
I have just created a new List to avoid the error, but one field is still giving me an error. I would appreciate if anyone tells me either how to fix this error or convert the objects to a DTO objects so they can be transferred.
Controller code :
#RequestMapping(value = "/findgroupnotes/{days}/{canvasid}")
public #ResponseBody List<GroupNotes> findGroupNotesByDays(#PathVariable("days")int days, #PathVariable("canvasid")int canvasid){
List<GroupNotes> groupNotesList = this.groupNotesService.findGroupNotesByDays(days,canvasid);
List<GroupNotes> toSendList = new ArrayList<>();
for(GroupNotes groupNotes : groupNotesList){
GroupNotes toSendNotes = new GroupNotes();
toSendNotes.setMnotecolor(groupNotes.getMnotecolor());
toSendNotes.setNoteCreationTime(groupNotes.getNoteCreationTime());
toSendNotes.setMnotetag(groupNotes.getMnotetag());
toSendNotes.setMnotetext(groupNotes.getMnotetext());
toSendNotes.setAttachCount(groupNotes.getAttachCount());
toSendNotes.setNoteDate(groupNotes.getNoteDate());
toSendList.add(toSendNotes);
}
return toSendList;
}
GroupNotesDAOImpl :
#Override
public List<GroupNotes> searchNotesByDays(int days, int mcanvasid) {
Session session = this.sessionFactory.getCurrentSession();
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_YEAR, -days);
long daysAgo = cal.getTimeInMillis();
Timestamp nowMinusDaysAsTimestamp = new Timestamp(daysAgo);
GroupCanvas groupCanvas = (GroupCanvas) session.get(GroupCanvas.class,mcanvasid);
Query query = session.createQuery("from GroupSection as n where n.currentcanvas.mcanvasid=:mcanvasid");
query.setParameter("mcanvasid", mcanvasid);
List<GroupSection> sectionList = query.list();
List<GroupNotes> notesList = new ArrayList<GroupNotes>();
for (GroupSection e : sectionList) {
System.out.println("Section name is "+e.getMsectionname());
GroupSection groupSection = (GroupSection) session.get(GroupSection.class,e.getMsectionid());
Query query1 = session.createQuery("from GroupNotes as gn where gn.ownednotes.msectionid=:msectionid and gn.noteCreationTime >:limit");
query1.setParameter("limit", nowMinusDaysAsTimestamp);
query1.setParameter("msectionid",e.getMsectionid());
notesList.addAll(query1.list());
}
// I am getting the data below, but I get JSON errors.
for(GroupNotes groupNotes : notesList){
System.out.println("Group notes found are "+groupNotes.getMnotetext());
}
return notesList;
}
GroupCanvas model :
#Entity
#Table(name = "membercanvas")
public class GroupCanvas{
#OneToMany(mappedBy = "currentcanvas",fetch=FetchType.LAZY, cascade = CascadeType.REMOVE)
#JsonIgnore
private Set<GroupSection> ownedsection = new HashSet<>();
#JsonIgnore
public Set<GroupSection> getOwnedsection() {
return this.ownedsection;
}
public void setOwnedsection(Set<GroupSection> ownedsection) {
this.ownedsection = ownedsection;
}
}
GroupSection model :
#Entity
#Table(name = "membersection")
public class GroupSection{
#OneToMany(mappedBy = "ownednotes", fetch = FetchType.EAGER,cascade = CascadeType.REMOVE)
#JsonIgnore
private Set<GroupNotes> sectionsnotes = new HashSet<>();
public Set<GroupNotes> getSectionsnotes(){
return this.sectionsnotes;
}
public void setSectionsnotes(Set<GroupNotes> sectionsnotes){
this.sectionsnotes=sectionsnotes;
}
}
GroupNotes model :
#Entity
#Table(name="groupnotes")
public class GroupNotes{
#Id
#Column(name="mnoteid")
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "mnote_gen")
#SequenceGenerator(name = "mnote_gen",sequenceName = "mnote_seq")
#org.hibernate.annotations.Index(name = "mnoticesidindex")
private int mnoticesid;
#Column(name = "mnotetext")
private String mnotetext;
#Column(name = "mnoteheadline")
private String mnotetag;
#Column(name = "mnotecolor")
private String mnotecolor;
#Column(name = "mnoteorder")
private double mnoteorder;
#Column(name = "attachmentcount")
private int attachCount;
#Column(name = "notedate")
private String noteDate;
#Column(name = "uploader")
private String uploader;
#Column(name = "activeedit")
private boolean activeEdit;
#Column(name = "notedisabled")
private boolean noteDisabled;
#Column(name = "noteinactive")
private boolean noteInActive;
#Column(name = "notecreatoremail")
private String noteCreatorEmail;
#Column(name = "prefix")
private String prefix;
#Column(name = "timestamp")
private Timestamp noteCreationTime;
#Transient
private boolean notRead;
#Transient
private String tempNote;
#Transient
private String canvasUrl;
#ManyToOne
#JoinColumn(name = "msectionid")
#JsonIgnore
private GroupSection ownednotes;
#JsonIgnore
public GroupSection getOwnednotes(){return this.ownednotes;}
public void setOwnednotes(GroupSection ownednotes){this.ownednotes=ownednotes;}
#JsonIgnore
public int getOwnedSectionId(){
return this.ownednotes.getMsectionid();
}
#OneToMany(mappedBy = "mnotedata",fetch = FetchType.LAZY,cascade = CascadeType.REMOVE)
#JsonIgnore
private Set<GroupAttachments> mattachments = new HashSet<>();
public Set<GroupAttachments> getMattachments() {
return this.mattachments;
}
public void setMattachments(Set<GroupAttachments> mattachments) {
this.mattachments = mattachments;
}
#OneToMany(mappedBy = "mhistory",fetch = FetchType.LAZY,cascade = CascadeType.REMOVE)
#JsonIgnore
private Set<GroupNoteHistory> groupNoteHistorySet = new HashSet<>();
public Set<GroupNoteHistory> getGroupNoteHistorySet(){
return this.groupNoteHistorySet;
}
public void setGroupNoteHistorySet(Set<GroupNoteHistory> groupNoteHistorySet){
this.groupNoteHistorySet = groupNoteHistorySet;
}
#OneToMany(mappedBy = "unreadNotes",fetch = FetchType.LAZY,cascade = CascadeType.REMOVE)
#JsonIgnore
private Set<UnreadNotes> unreadNotesSet = new HashSet<>();
public Set<UnreadNotes> getUnreadNotesSet(){
return this.unreadNotesSet;
}
public void setUnreadNotesSet(Set<UnreadNotes> unreadNotesSet){
this.unreadNotesSet = unreadNotesSet;
}
//getters and setters ignored
}
Error log :
org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.journaldev.spring.model.GroupNotes["ownedSectionId"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0]->com.journaldev.spring.model.GroupNotes["ownedSectionId"])
Kindly let me know what to do, as I am stuck on that error since some time.
What I think that happens is Jackson tries to serialize all fields in the hierarchy based on getter methods. In some situation NullPointerException is thrown in the following method:
#JsonIgnore
public int getOwnedSectionId(){
return this.ownednotes.getMsectionid();
}
replace it with the following method:
#JsonIgnore
public int getOwnedSectionId(){
if(this.ownednotes != null)
return this.ownednotes.getMsectionid();
return 1;
}
I don't have an explanation why jackson tries to serialize it when is market with #JsonIgnore but you can give a try with my proposal
I would appreciate if anyone tells me either how to fix this error or convert the objects to a DTO objects so they can be transferred.
We use DozerMapper at work for this purpose.
Instead of doing that mapping manually you might want to take a look at Blaze-Persistence Entity Views which can be used to efficiently implement the DTO pattern with JPA. Here a quick code sample how your problem could be solved
First you define your DTO as entity view
#EntityView(GroupNotes.class)
public interface GroupNoteView {
#IdMapping("mnoticesid") int getId();
String getMnotecolor();
String getMnotetag();
String getMnotetext();
String getNoteDate();
Timestamp getNoteCreationTime();
int getAttachCount();
}
Next you adapt your DAO to make use of it
#Override
public List<GroupNoteView> searchNotesByDays(int days, int mcanvasid) {
EntityManager entityManager = // get the entity manager from somewhere
CriteriaBuilderFactory cbf = // factory for query building from Blaze-Persistence
EntityViewManager evm = // factory for applying entity views on query builders
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_YEAR, -days);
long daysAgo = cal.getTimeInMillis();
Timestamp nowMinusDaysAsTimestamp = new Timestamp(daysAgo);
CriteriaBuilder<GroupNotes> cb = cbf.create(entityManager, GroupNotes.class, "note")
.where("noteCreationTime").gt(nowMinusDaysAsTimestamp)
.where("ownednotes.ownedcanvas.mcanvasid").eq(mcanvasid);
return evm.applySetting(EntityViewSetting.create(GroupNoteView.class), cb)
.getResultList();
}
And finally the calling code
#RequestMapping(value = "/findgroupnotes/{days}/{canvasid}")
public #ResponseBody List<GroupNoteView> findGroupNotesByDays(#PathVariable("days")int days, #PathVariable("canvasid")int canvasid){
return this.groupNotesService.findGroupNotesByDays(days, canvasid);
}
Lets say I have a query :
SELECT one FROM EntityOne one, EntityTwo two
WHERE one.id = two.otherId AND two.someValue = 2
I'd like to transform it using Criteria tools but don't know to how fetch
thanks in advance
Here is the code on how you can get results using Criteria:
EntityOne.java
#Entity
public class EntityOne {
#Id
#GeneratedValue
private int id;
private String name;
#OneToMany(mappedBy = "entity", cascade=CascadeType.ALL)
private Set<EntityTwo> entities = new HashSet<EntityTwo>();
public EntityOne(String name) {
this.name = name;
}
public void addEntity(EntityTwo entity) {
this.entities.add(entity);
}
// Default constructor, setters & getters
}
EntityTwo.java
#Entity
public class EntityTwo {
#Id
#GeneratedValue
int id;
String name;
#ManyToOne
#JoinColumn(name = "entity_one_id")
private EntityOne entity;
public EntityTwo(String name) {
this.name = name;
}
// Default constructor, setters & getters
}
Code to save some entities to database:
EntityOne eo1 = new EntityOne("Entity eo1");
EntityTwo et1 = new EntityTwo("one");
EntityTwo et2 = new EntityTwo("two");
eo1.addEntity(et1);
eo1.addEntity(et2);
et1.setEntity(eo1);
et2.setEntity(eo1);
EntityOne eo2 = new EntityOne("Entity eo2");
EntityTwo et3 = new EntityTwo("three");
EntityTwo et4 = new EntityTwo("four");
eo2.addEntity(et3);
eo2.addEntity(et4);
et3.setEntity(eo2);
et3.setEntity(eo2);
session.save(eo1);
session.save(eo2);
Now the code for getting records using Criteria:
Criteria criteria = session.createCriteria(EntityOne.class, "e1");
criteria.createAlias("e1.entities", "e2");
criteria.add(Restrictions.eq("e2.name", "two"));
List<EntityOne> entityList = criteria.list();
for (EntityOne entityOne : entityList) {
System.out.println(entityOne.getName());
for (EntityTwo entity : entityOne.getEntities()) {
System.out.println("->" + entity.getName());
}
}
Output will be:
Entity eo1
->two
->one