merge如何避免LazyInitException?
How does merge avoid LazyInitException?
这是我的 PersistenceUtil class..
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(电影); 据我了解。我们正在传递一个分离的电影对象,我们将它合并到 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(film) 应该从数据库加载电影并将其同步到持久性上下文吗?然而我所看到的是:
java.lang.NullPointerException
at java.util.ArrayList.addAll(ArrayList.java:559)
at biz.tugay.learningjpa.service.ActorServiceImpl.getActorsOfFilm(ActorServiceImpl.java:57)
所以我很困惑,我在第一种情况和第二种情况下的分离实体有什么区别?不合并只与 id 一起工作?
merge(film)
从数据库中获取具有给定电影 ID 的电影(我们称之为 managedFilm
),将所有状态从 film
复制到 managedFilm
,然后returnsmanagedFim
。
由于 actors
列表默认为 null,并且由于此 null 字段被复制到 managedFilm
,对其进行迭代会抛出 NullPointerException。
你不应该使用 merge()
,除非你真的打算将分离胶片的状态复制到托管胶片(并因此可能通过调用 getActorsOfFilm()
来修改胶片,这将然后真的很糟糕的名字并且有两个非常不同的责任)。只需传递电影 ID 而不是电影,然后使用 EntityManager.find()
.
从数据库中获取托管电影
这是我的 PersistenceUtil class..
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(电影); 据我了解。我们正在传递一个分离的电影对象,我们将它合并到 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(film) 应该从数据库加载电影并将其同步到持久性上下文吗?然而我所看到的是:
java.lang.NullPointerException
at java.util.ArrayList.addAll(ArrayList.java:559)
at biz.tugay.learningjpa.service.ActorServiceImpl.getActorsOfFilm(ActorServiceImpl.java:57)
所以我很困惑,我在第一种情况和第二种情况下的分离实体有什么区别?不合并只与 id 一起工作?
merge(film)
从数据库中获取具有给定电影 ID 的电影(我们称之为 managedFilm
),将所有状态从 film
复制到 managedFilm
,然后returnsmanagedFim
。
由于 actors
列表默认为 null,并且由于此 null 字段被复制到 managedFilm
,对其进行迭代会抛出 NullPointerException。
你不应该使用 merge()
,除非你真的打算将分离胶片的状态复制到托管胶片(并因此可能通过调用 getActorsOfFilm()
来修改胶片,这将然后真的很糟糕的名字并且有两个非常不同的责任)。只需传递电影 ID 而不是电影,然后使用 EntityManager.find()
.