سؤال

I've got two model classes:

package com.me.model;

import java.util.Objects;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;


@Entity
public class Post {

    private Integer id;
    private String text;
    private Person person;

    @Id
    @GeneratedValue
    @Column(name = "ID")
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    @ManyToOne(cascade = {CascadeType.ALL})
    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    @Override
    public String toString() {
        return String.format("\nThe id of a post is: %d, written by person %s, containing a text: %s\n", this.id, this.person.toString(), this.text);
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 89 * hash + Objects.hashCode(this.id);
        hash = 89 * hash + Objects.hashCode(this.text);
        hash = 89 * hash + Objects.hashCode(this.person);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Post other = (Post) obj;
        if (!Objects.equals(this.id, other.id)) {
            return false;
        }
        if (!Objects.equals(this.text, other.text)) {
            return false;
        }
        if (!Objects.equals(this.person, other.person)) {
            return false;
        }
        return true;
    }

}


package com.me.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "PERSON")
public class Person implements Serializable {

    private Integer id;
    private String name;
    private String email;
    private List<Post> posts;

    @OneToMany(mappedBy = "person", fetch = FetchType.EAGER,
            cascade = {CascadeType.ALL})
    public List<Post> getPosts() {
        return posts;
    }

    public void setPosts(List<Post> posts) {
        this.posts = posts;
    }

    public void addPost(Post post) {
        post.setPerson(this);
        if (posts == null) {
            posts = new ArrayList<Post>();
        }
        posts.add(post);
    }

    @Id
    @GeneratedValue
    @Column(name = "ID")
    public Integer 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 String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "Person [id=" + id + ", name=" + name + ", email=" + email + ", no of posts: " + this.posts.size() + "]";
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 17 * hash + Objects.hashCode(this.id);
        hash = 17 * hash + Objects.hashCode(this.name);
        hash = 17 * hash + Objects.hashCode(this.email);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Person other = (Person) obj;
        if (!Objects.equals(this.id, other.id)) {
            return false;
        }
        if (!Objects.equals(this.name, other.name)) {
            return false;
        }
        if (!Objects.equals(this.email, other.email)) {
            return false;
        }
        return true;
    }

}

and a test class:

public static final Logger log = Logger.getLogger(Main.class.getName());

    public static void main(String[] args) {
        log.info("************** BEGINNING PROGRAM **************");

        ApplicationContext context = new ClassPathXmlApplicationContext("WEB-INF/spring-config.xml");
        PersonService personService = (PersonService) context.getBean("personService");
        PostService postService = (PostService) context.getBean("postService");

        Person person = new Person();
        person.setName("me");
        person.setEmail("me@box.com");

        Post post = new Post();
        String text = "bbbbbbbbbbbbbbbbbbbbbbbbb";
        post.setText(text);

        Person personFromDB = personService.getPersonFromDB(person.getName(), person.getEmail());
        if (personFromDB == null) {
            log.info("No person in DB!! ");
            person.addPost(post);
            personService.addPerson(person);
        } else {
            log.info("Person " + personFromDB + " is being found in DB!");
//            personService.getPersonDao().getEntityManager().detach(personFromDB);
            personFromDB.addPost(post);


 //            personService.addPerson(personFromDB);
            personService.updatePerson(personFromDB);
        }

        List<Person> persons = personService.fetchAllPersons();
        log.info("\nThe list of all persons = " + persons);

        List<Post> posts = postService.fetchAllPosts();
        log.info("\nThe list of all posts = " + posts);

        log.info("************** ENDING PROGRAM *****************");
    }

And offcourse DAO and Service classes (entityManager injected by Spring). Now I'd like to update a person object from DB(after addition of another post into it). When debuging the program I can see new post added to arraylist. But when calling personService.updatePerson (which in turn calls getPersonDao().getEntityManager().merge(person); ) nothing happens! Still getting the same object with only one item in collecion persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    version="1.0">
    <persistence-unit name="personPersistenceUnit" transaction-type="RESOURCE_LOCAL" >
    <class>com.me.model.Person</class>
    <class>com.me.model.Post</class>
    <properties>
        <property name="hibernate.hbm2ddl.auto" value="update"/>
    </properties>
</persistence-unit>
</persistence> 

service classes:

import com.me.dao.PersonDao;
import com.me.model.Person;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class PersonService {

    private PersonDao personDao;

    public PersonDao getPersonDao() {
        return personDao;
    }
    @Autowired
    public void setPersonDao(PersonDao personDao) {
        this.personDao = personDao;
    }

    public void addPerson(Person person) {
        getPersonDao().insert(person);
    }

    public void updatePerson(Person person){
        getPersonDao().getEntityManager().merge(person);
    }

    public List<Person> fetchAllPersons() {
        return getPersonDao().selectAll();
    }

    public boolean isPersonExist(Person person){
        if(personDao.getPersonByNameAndEMail(person.getName(), person.getEmail()) != null){
            return true;
        }
        return false;
    }

    public Person getPersonFromDB(String name, String email){
        return personDao.getPersonByNameAndEMail(name, email);
    }
}

package com.me.service;

import com.me.dao.PostDao;
import com.me.model.Post;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component
public class PostService {

    private PostDao personDao;

    public PostDao getPostDao() {
        return personDao;
    }

    @Autowired
    public void setPostDao(PostDao personDao) {
        this.personDao = personDao;
    }

    public void addPost(Post person) {
        getPostDao().insert(person);
    }

    public void merge(Post post){
        getPostDao().getEntityManager().merge(post);
    }

    public List<Post> fetchAllPosts() {
        return getPostDao().selectAll();
    }
}

dao classes:

package com.me.dao;

import com.me.model.Person;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Repository("personDao")
@Transactional(propagation = Propagation.REQUIRED)
public class PersonDao {

    private static final String SELECT_QUERY = "select p from Person p";

    @PersistenceContext
    private EntityManager entityManager;

    public EntityManager getEntityManager() {
        return entityManager;
    }

    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public void insert(Person person) {
        if(getPersonByNameAndEMail(person.getName(), person.getEmail()) != null){
            return;
        }
        entityManager.persist(person);
    }

    public List<Person> selectAll() {
        Query query = entityManager.createQuery(SELECT_QUERY);
        List<Person> persons = (List<Person>) query.getResultList();
        return persons;
    }

    public Person getPersonByNameAndEMail(String name, String email) {
        String hql = "select p from Person p where p.name = :theName and p.email = :email";
        Query q = entityManager.createQuery(hql);
        q.setParameter("theName", name);
        q.setParameter("email", email);
        q.setMaxResults(1);
        Person p;
        try{
            p = (Person) q.getSingleResult();
        }catch(NoResultException e){
            p = null;
        }
        return p;
    }

}

package com.me.dao;

import com.me.model.Post;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


@Repository("potDao")
@Transactional(propagation = Propagation.REQUIRED)
public class PostDao {
    private static final String SELECT_QUERY = "select p from Post p";

    @PersistenceContext
    private EntityManager entityManager;

    public EntityManager getEntityManager() {
        return entityManager;
    }

    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public void insert(Post person) {
        entityManager.persist(person);
    }

    public List<Post> selectAll() {
        Query query = entityManager.createQuery(SELECT_QUERY);
        List<Post> persons = (List<Post>) query.getResultList();
        return persons;
    }
}
هل كانت مفيدة؟

المحلول

One problem I noticed is related to how you initialized the children collection.

You don't declare a null collection association:

private List<Post> posts;

But you do it like this:

private List<Post> posts = new ArrayList();

Then the addPost becomes:

public void addPost(Post post) {
    post.setPerson(this);
    posts.add(post);
}

Hibernate will always replace the posts ArrayList with a proxy and this way you are sure you want get NullPointerException when accessing the getPosts() without a call to addPost().

Also the Post is the child entity, since it requires a Person first. You don't usually cascade from children to parents, so in the Post entity you can remove the cascade to Parent:

@ManyToOne
public Person getPerson() {
    return person;
}

And it's not a good idea to rely on the id in equals and hashCode because of this.

Calling merge and having cascade in Parent should propagate the change to the database. since the bidirectional associations are set by addPost.

Are your service method marked with @Transactional?

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top