0

I guess the problem I am having is common and I have already searched for similar questions and tried solutions, none of them work. I am working on a Spring-MVC application which uses hibernate. The app has 2 classes(also 2 tables), Person has one-to-many relationship with Notes. I am trying to add a note, and I am getting the following error.

Error log :

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.journaldev.spring.model.Person
    org.hibernate.engine.internal.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:294)
    org.hibernate.type.EntityType.getIdentifier(EntityType.java:537)
    org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:311)
    org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:321)
    org.hibernate.type.TypeHelper.findDirty(TypeHelper.java:294)
    org.hibernate.persister.entity.AbstractEntityPersister.findDirty(AbstractEntityPersister.java:4243)
    org.hibernate.event.internal.DefaultFlushEntityEventListener.dirtyCheck(DefaultFlushEntityEventListener.java:546)
    org.hibernate.event.internal.DefaultFlushEntityEventListener.isUpdateNecessary(DefaultFlushEntityEventListener.java:232)
    org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:159)
    org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:231)
    org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:102)
    org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:55)
    org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1222)
    org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)

Person model :

@Entity
@Table(name="person")
public class Person implements UserDetails{

    private static final GrantedAuthority USER_AUTH = new SimpleGrantedAuthority("ROLE_USER");

    @Id
    @Column(name="personid")
    @GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "person_seq_gen")
    @SequenceGenerator(name = "person_seq_gen",sequenceName = "person_seq")
    private int id;

  @OneToMany(cascade = CascadeType.ALL,mappedBy = "person1")
    private Set<Notes> notes1;

Notes model :

   @Entity
    @Table(name="note")
    public class Notes {

        @Id
        @Column(name="noteid")
        @GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "note_gen")
        @SequenceGenerator(name = "note_gen",sequenceName = "note_seq")
        private int noteId;

    @ManyToOne
       @JoinColumn(name = "personid")
       private Person person1;

  public Person getPerson1() {
        return person1;
    }

    public void setPerson1(Person person1) {
        this.person1 = person1;
    }

As I am using Spring security too, I am able to set a person for the JSP file where I want to add notes as below :

Notes Controller :

@Controller
public class NoteController {



    private NotesService notesService;

    @Autowired(required=true)
    @Qualifier(value="notesService")
    public void setNotesService(NotesService notesService){this.notesService=notesService;}

  @RequestMapping(value= "/note/add", method = RequestMethod.GET)
    public String addNote(@ModelAttribute("notes") Notes p,@AuthenticationPrincipal Person person){
        p.setPerson1(person);
        this.notesService.addNote(p);
        return "redirect:/";
    }

NotesDAOimpl :

@Transactional
@Repository
public class NotesDAOImpl implements NotesDAO{



    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sf){
        this.sessionFactory = sf;
    }


    @Override
    public void addNote(Notes notes, int id) {
 Person person;
        notes.setPerson1(person);

       Session session = this.sessionFactory.getCurrentSession();
       session.saveOrUpdate(notes);
    }

PersonDAOImpl

@Transactional
@Repository
public class PersonDAOImpl implements PersonDAO {



    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sf){
        this.sessionFactory = sf;
    }

    @Override
    public void addPerson(Person p) {
        Notes notes;
        p.setNotes1(notes);
        Session session = this.sessionFactory.getCurrentSession();
        session.saveOrUpdate(p);

    }

This code right now only creates blank Persons in database, if i stop saving the person, then I get the above mentioned error. Any pointers would be good. Thank you.

We are Borg
  • 5,117
  • 17
  • 102
  • 225

2 Answers2

0

You should probably set the association between the Person and Notes on both sides, e.g. person.setNotes(..), notes.setPersonId(...)

If you specify the association between your object this way, the save cascade should work properly and both sides of the association should be saved in the DB.

Good luck

  • Thank you for your reply. Do you mean getters and setters for mapping? – We are Borg Oct 23 '14 at 09:21
  • You definitely need getter and setter on both classes. But what I mean is that before calling session.save(person), you should do person.setNotes(notes) and notes.setPerson(person) and then let hibernate do its cascade "magic". Hope it makes sense now! – Ioannis Sermetziadis Oct 23 '14 at 09:30
  • Hello, I did as you said, it makes sense, unfortunately, either I am getting cast errors when I initialize individual classes or no initialization error. I have updated post with NoteDAOImpl and PersonDAOImpl, is it correct? – We are Borg Oct 23 '14 at 09:38
  • The solution you have included doesn't look correct to me. I don't know why you are have created 2 DAO classes and save the entities separately. Btw, this code will always fail "Person person; notes.setPerson1(person);" will definitely cause initialization exception. So, I would suggest you to work on a single DAO that will set the association between your objects and save the only own side (preferable the owner of the association, which seems to be the Notes in your case) – Ioannis Sermetziadis Oct 23 '14 at 09:47
  • Actually it does not make sense to call person.setNotes(notes) and notes.setPerson(person) in OneToMany or ManyToOne relationship. If it is set up properly with annotations then only relation owner is updated. In this case it is enough to call notes.setPerson(person) and hibernate does the work. – Grzegorz Solecki Oct 23 '14 at 09:51
  • Thanks for the comment @gsolecki : But I am unable to use the line Person person; notes.setPerson1(person) as it says person is uninitialized, when i initialize it to new Person(); I get cast errors. – We are Borg Oct 23 '14 at 10:00
  • If notes.setPerson1(person) says person is uninitialized then it looks like your person is not authenticated so spring injects null to @AuthenticationPrincipal Person person. Make sure first you have a person in DB and the person is logged in to your webapp with spring-security properly. Also try to do some junit testing on repository (service) level using dummy person and notes. Also have a look at examples here how to make a proper web app with spring-data-jpa http://spring.io/guides/gs/accessing-data-jpa/ ... maybe it will inspire you. – Grzegorz Solecki Oct 23 '14 at 10:38
  • Hello @gsolecki : The error of not initialized I am getting is in the IDE.. Spring security is working fine as I have already checked by checking login links, cookies, spring-security redirections, etc. Also there is persistent login which is working too. – We are Borg Oct 23 '14 at 11:15
  • Your problem is very broad and there is no one particular place where someone could help you. I suggest you start with the analysis of a simple example that works such as this here https://github.com/spring-projects/spring-security/tree/master/samples or here https://github.com/spring-projects/spring-data-examples/tree/master/jpa and then build up your skills by experimenting with different settings and more difficult things. – Grzegorz Solecki Oct 23 '14 at 11:28
  • Also, the link you provided me involves entering entries manually through static void main, that is not what I am doing, entries are added dynamically by the user, not by developer as seperate and then writing code to commit in DB. – We are Borg Oct 23 '14 at 11:29
0

There are many different types of @OneToMany and @ManyToOne relationships and it depends not only on which side manages the reference but also if the relation is unidirectional or bidirectional. Please have a look here what-is-the-difference-between-unidirectional-and-bidirectional-associations and here How to map collections

Community
  • 1
  • 1
Grzegorz Solecki
  • 308
  • 1
  • 11