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.
Related
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
}
I have a standard many to many relationship in Spring with persistence using Hibernate. Constructors and setters left out for brevity.
#Entity
#Table(name = "a")
public class A {
#ManyToMany(mappedBy = "as")
private Set<B> bs;
}
#Entity
#Table(name = "bs")
public class B {
#ManyToMany
#JoinTable(
name = "b_a",
joinColumns = #JoinColumn(name = "b_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "a_id", referencedColumnName = "id")
)
private Set<A> as = new HashSet<>();
}
I also have DTOs for each of these classes and I have static methods to transfer between them. Due to the many-to-many relationship, I need to implement a form of state tracker to stop the program getting caught in infinite recursion.
public final class DTOEntityMapper {
private static class StateHolder {
private List<Object> alreadySeenObjects = new ArrayList<>();
void addObject(Object o){
alreadySeenObjects.add(o);
}
boolean contains(Object o){
return alreadySeenObjects.contains(o);
}
}
public static BDTO fromBToDTO(B b) {
return fromBToDTO(b, new StateHolder());
}
private static BDTO fromBToDTO(B b, StateHolder state) {
state.addObject(b);
return BDTO.builder()
.withAs(b.getAs().stream().filter(item -> !state.contains(item)).map(e -> fromAToDTO(e, state)).collect(Collectors.toSet()))
.build();
}
}
// Note: fromAToDTO does same thing.
However when I try and test I get an assertion failure:
#Test
public void convertFromBToBDTO() {
A a = new A("a");
B b = new B("b");
b.setAs(ImmutableSet.of(a));
a.setBs(ImmutableSet.of(b));
aDTO ADTO = new ADTO("a");
bDTO BDTO = new BDTO("b");
bDTO.setAs(ImmutableSet.of(aDTO));
aDTO.setBs(ImmutableSet.of(bDTO));
BDTO expected = bDTO;
assertThat(DTOEntityMapper.fromBToDTO(b)).isEqualTo(expected);
}
Actual: BDTO{as=[ADTO{bs=}]}
Expected: BDTO{as=[ADTO{bs=b1}]}
Basically I am confused with how I deal with many-to-many relationships when converting between the data model and DTOs. Hopefully this makes sense!
I have 1:m relation. I post data about "1" and also about "m" relation in one post. What i am trying to achieve is to insert data ( m ) into "1" , then persist 1 into database which should create info in database about 1 and about m.
The "1" Enitity:
private List<OptionEntity> options;
#OneToMany(mappedBy = "survey", cascade = CascadeType.MERGE)
public List<OptionEntity> getOptions() {
return options;
}
public void setOptions(List<OptionEntity> options) {
this.options= options;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "survey_id", nullable = false)
public int getSurveyId() {
return surveyId;
}
public void setSurveyId(int surveyId) {
this.surveyId = surveyId;
}
the "m" entitites
private SurveyEntity survey;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="survey_id")
public SurveyEntity getSurvey() {
return survey;
}
public void setSurvey(SurveyEntity survey ) {
this.survey = survey;
}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "option_id", nullable = false)
public int getOptionId() {
return optionId;
}
public void setOptionId(int optionId) {
this.optionId = optionId;
}
However when i do
List<OptionEntity> ops = new ArrayList<>();
for( String option : options ){
OptionEntity tmp_option = new OptionEntity();
tmp_option.setText( option );
ops.add(tmp_option);
}
survey.setOptions(ops);
surveyDAO.add(survey);
when add is
public void add ( SurveyEntity s )
{
em.persist( s );
}
Creates only record for "1" entity in database. The records for all "m" entities are not inserted in the databases.
I thought whats important here is identity set to AUTO for m entities so database can create their id ( it has autoincrement ).
Seems i am wrong in this one.
What is the correct way to insert into 1:m relation at once?
Thanks for help
You have to do two things:
1) Set the relationship on both sides, so in the loop add the Survey entity to each of the Option entities:
for( String option : options ){
OptionEntity tmp_option = new OptionEntity();
tmp_option.setText( option );
ops.add(tmp_option);
tmp_option.setSurvey(survey);
}
2) Either use em.merge() instead of em.persist() or add this cascade option:
#OneToMany(mappedBy = "survey", cascade = {CascadeType.MERGE, CascadeType.PERSIST})
public List<OptionEntity> getOptions() {
return options;
}
I try to get back a list of elements in an instance Criteria. In the execution I obtain this exception. What is the problem ?
The name of my database is "TransPlusBD".
The name of my table is a "gerant".
But the error indicates me that he(it) does not find the table "TransPlusDB.gerant_gerant", yet this table does not exist.
Normally we have to have his "TransPlusDB.gerant".
Code of the configuration
properties = new Properties();
properties.put("hibernate.dialect","org.hibernate.dialect.MySQLInnoDBDialect");
properties.put("hibernate.connection.driver_class","com.mysql.jdbc.Driver");
properties.put("hibernate.connection.url","jdbc:mysql://(cloud amazone aws).amazonaws.com:3306/TransPlusDB");
properties.put("hibernate.connection.username","xxx");
properties.put("hibernate.connection.password","xxxxxxxxxxxx");
properties.put("hibernate.connection.pool_size","4");
configuration = new Configuration();
configuration.setProperties(properties);
configuration.addAnnotatedClass(Administrator.class);
configuration.addAnnotatedClass(AutoGare.class);
configuration.addAnnotatedClass(Car.class);
configuration.addAnnotatedClass(City.class);
configuration.addAnnotatedClass(Company.class);
configuration.addAnnotatedClass(transplus.models.Configuration.class);
configuration.addAnnotatedClass(DateDeparture.class);
configuration.addAnnotatedClass(Departure.class);
configuration.addAnnotatedClass(HoursDeparture.class);
configuration.addAnnotatedClass(Luggage.class);
configuration.addAnnotatedClass(Manager.class);
configuration.addAnnotatedClass(Passenger.class);
configuration.addAnnotatedClass(PlanningVoyage.class);
configuration.addAnnotatedClass(Route.class);
configuration.addAnnotatedClass(Stopover.class);
configuration.addAnnotatedClass(SysAdmin.class);
configuration.addAnnotatedClass(Ticket.class);
configuration.addAnnotatedClass(TypeCar.class);
configuration.addAnnotatedClass(ModificationLuggage.class);
configuration.addAnnotatedClass(ModificationTicket.class);
configuration.addAnnotatedClass(PassageRoute.class);
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties()).build();
Code of my class
#Entity
#Table(name = "gerant")
public class Manager implements Serializable // Table Gerant
{
#Id
#GeneratedValue
#Column(name = "code_gerant")
private long code_manager;
#Column(name = "matricule_gérant",unique = true)
private String matricule_manager;
#Column(name = "nom_gerant")
private String lastName_manager;
#Column(name = "prenom_gerant")
private String firstName_manager;
#Column(name = "password_gerant",nullable = false)
private String password_manager;
#Column(name = "login_gerant",unique = true,nullable = false)
private String login_manager;
#Column(name = "poste_gerant")
private String function_manager;
#Column(name = "actif_gerant")
private boolean enabled_manager;
#Enumerated(EnumType.ORDINAL)
#Column(name = "privilege_gerant")
private Privilege privilege;
#ManyToOne
#JoinColumn(name = "code_manager", foreignKey = #ForeignKey(name = "fk_gerant_manager"))
private Administrator administrator;
#ManyToOne
#JoinColumn(name = "over_gerant", foreignKey = #ForeignKey(name = "fk_over_gerant"))
private Manager overManager;
#Expose // Annotation for Gson
#OneToMany(cascade = CascadeType.ALL,orphanRemoval = false)
private List<Manager> underManagers = new ArrayList<>();
Code for the recovery of the list.
public String getAllManager()
{
if(session.isOpen())
{
Transaction transaction = null;
try
{
transaction = session.beginTransaction();
transaction.begin();
Criteria criteria = session.createCriteria(Manager.class);
List list = criteria.list();
transaction.commit();
if(list != null)
{
if(!list.isEmpty())
return serializeTab(list);
}
return null;
}
catch (Exception e)
{
if(transaction != null)
transaction.rollback();
e.printStackTrace();
}
}
return null;
}
Here is the raised exception
this = {ServiceManager#3681}
transaction = {TransactionImpl#3683}
transactionCoordinator = {JdbcResourceLocalTransactionCoordinatorImpl#3979}
transactionDriverControl = {JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl#3980}
valid = false
e = {SQLGrammarException#3954} "org.hibernate.exception.SQLGrammarException: could not extract ResultSet"
sqlException = {MySQLSyntaxErrorException#3958} "com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'TransPlusDB.gerant_gerant' doesn't exist"
SQLState = "42S02"
vendorCode = 1146
next = null
detailMessage = "Table 'TransPlusDB.gerant_gerant' doesn't exist"
cause = {MySQLSyntaxErrorException#3958} "com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'TransPlusDB.gerant_gerant' doesn't exist"
stackTrace = {StackTraceElement[0]#3961}
suppressedExceptions = {Collections$UnmodifiableRandomAccessList#3962} size = 0
sql = "n/a"
Help I PLEASE
The table gerant_gerant is a join table for a self association for this
#OneToMany(cascade = CascadeType.ALL,orphanRemoval = false)
private List<Manager> underManagers = new ArrayList<>();
You need to let Hibernate to create this table using the hibernate.hbm2ddl.auto property or you can create it manually with constraints ( a foreign key, a unique key).
You can specify a join table name with the #JoinTable annotation.
I added this line of code at the level of the configuration
properties.put("hbm2ddl.auto","validate");
Then this code at the level of my class
#OneToMany(cascade = CascadeType.ALL,orphanRemoval = false,mappedBy = "overManager")
private List<Manager> underManager;
Thank
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);
}