合并如何避免LazyInitException?

时间:2016-10-23 14:10:52

标签: java hibernate jpa lazy-initialization

这是我的PersistenceUtil类..

package biz.tugay.learningjpa.persistence;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class PersistenceUtilImpl implements PersistenceUtil {

    private EntityManagerFactory entityManagerFactory;

    public PersistenceUtilImpl(final String persistenceUnit) {
        entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnit);
    }

    public EntityManager getEntityManager() {
        final EntityManager entityManager = entityManagerFactory.createEntityManager();
        return entityManager;
    }

    public void closeFactory() {
        entityManagerFactory.close();
    }
}

这些是我的实体..

Actor.java

package biz.tugay.learningjpa.model;

import javax.persistence.*;
import java.util.List;

@Entity
public class Actor {

    private Integer id;
    private String firstname;
    private String lastname;

    private List<Film> films;

    @Id
    @Column(name = "actor_id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Integer getId() {
        return id;
    }

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

    @Basic
    @Column(name = "first_name")
    public String getFirstname() {
        return firstname;
    }

    public void setFirstname(String first_name) {
        this.firstname = first_name;
    }

    @Basic
    @Column(name = "last_name")
    public String getLastname() {
        return lastname;
    }

    public void setLastname(String last_name) {
        this.lastname = last_name;
    }

    @ManyToMany
    @JoinTable(name = "film_actor",
            joinColumns = @JoinColumn(name = "actor_id"),
            inverseJoinColumns = @JoinColumn(name = "film_id")
    )
    public List<Film> getFilms() {
        return films;
    }

    public void setFilms(List<Film> films) {
        this.films = films;
    }
}

Film.java

package biz.tugay.learningjpa.model;

import javax.persistence.*;
import java.util.List;

@Entity
public class Film {

    private Integer id;
    private String title;

    private List<Actor> actors;

    @Id
    @Column(name = "film_id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Integer getId() {
        return id;
    }

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

    @Basic
    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @ManyToMany
    @JoinTable(name = "film_actor",
            joinColumns = @JoinColumn(name = "film_id"),
            inverseJoinColumns = @JoinColumn(name = "actor_id"))
    public List<Actor> getActors() {
        return actors;
    }

    public void setActors(List<Actor> actors) {
        this.actors = actors;
    }
}

我的模特有2项服务:

ActorServiceImpl.java

public final class ActorServiceImpl implements ActorService {

    private final PersistenceUtil persistenceUtil;

    public ActorServiceImpl(PersistenceUtil persistenceUtil) {
        this.persistenceUtil = persistenceUtil;
    }

    @Override
    public List<Actor> getActorsOfFilm(Film film) {
        final EntityManager entityManager = persistenceUtil.getEntityManager();
        final Film mergedFilm = entityManager.merge(film);
        final ArrayList<Actor> actors = new ArrayList<Actor>();
        actors.addAll(mergedFilm.getActors());
        entityManager.close();
        return actors;
    }
}

FilmServiceImpl.java

public final class FilmServiceImpl implements FilmService {

    private final PersistenceUtil persistenceUtil;

    public FilmServiceImpl(final PersistenceUtil persistenceUtil) {
        this.persistenceUtil = persistenceUtil;
    }

    @Override
    public Film filmById(int filmId) {
        final EntityManager entityManager = persistenceUtil.getEntityManager();
        final Film film = entityManager.find(Film.class, filmId);
        entityManager.close();
        return film;
    }
}

我有2个测试,如下所示:

public class ActorServiceImplTest {

    private PersistenceUtilImpl persistenceUtil;
    private ActorServiceImpl actorService;

    @Before
    public void init() {
        persistenceUtil = new PersistenceUtilImpl("sakiladb");
        actorService = new ActorServiceImpl(persistenceUtil);
    }

    @After
    public void tearDown() {
        persistenceUtil.closeFactory();
    }

    @Test(expected = LazyInitializationException.class)
    public void testGetActorsOfFilmLazyInit() {
        final FilmServiceImpl filmService = new FilmServiceImpl(persistenceUtil);
        final Film film = filmService.filmById(1);
        for (Actor actor : film.getActors()) {
            System.out.println(actor.getFirstname());
        }
    }

    @Test
    public void testGetActorsOfFilm() {
        final FilmServiceImpl filmService = new FilmServiceImpl(persistenceUtil);
        final Film film = filmService.filmById(1);
        final List<Actor> actorsOfFilm = actorService.getActorsOfFilm(film);
        for (Actor actor : actorsOfFilm) {
            System.out.println(actor.getFirstname());
        }
    }
}

所以第一个测试会抛出一个LazyInitException,这很好..当我获得电影的演员时,EntityManager关闭了。

关于第二个测试,名为“testGetActorsOfFilm”。如图所示,它从数据库传递并在控制台中打印Actors。这是由 entityManager.merge(film); < / strong>据我所知。我们正在传递一个独立的电影对象,我们将它合并到PersistenceContext中,然后我们在实现中获得电影的演员。

但是,我想问的是,为什么这个测试失败了:

@Test
public void testGetActorsOfFilm() {
    final Film film = new Film();
    film.setId(1);
    final List<Actor> actorsOfFilm = actorService.getActorsOfFilm(film);
    for (Actor actor : actorsOfFilm) {
        System.out.println(actor.getFirstname());
    }
}

我的理解是,我正在创建一个ID为1的Film对象并将其传递给服务方法。那么entityManager.merge(电影)应该从数据库加载电影并将其同步到持久性上下文?但我所看到的只是:

java.lang.NullPointerException
    at java.util.ArrayList.addAll(ArrayList.java:559)
    at biz.tugay.learningjpa.service.ActorServiceImpl.getActorsOfFilm(ActorServiceImpl.java:57)

所以我很困惑,我在第一种情况下的分离实体与第二种情况有什么区别?不合并只与id一起工作吗?

1 个答案:

答案 0 :(得分:1)

merge(film)从数据库中获取带有给定电影ID的电影(我们称之为managedFilm),将所有状态从film复制到managedFilm,然后返回{ {1}}。

由于managedFim列表默认为null,并且由于此空字段被复制到actors,因此迭代它会抛出NullPointerException。

您不应该使用managedFilm,除非您的意图是将分离的电影的状态复制到托管电影(因此可能会通过调用merge()来修改电影,这将是真的很糟糕,有两个非常不同的职责)。只需传递电影ID而不是电影,然后使用getActorsOfFilm()从数据库中获取管理的电影。