Spring 引导 JPA 不将实体附加到会话
Spring boot JPA doesn't attach entities to the session
我有一个项目使用 Spring Boot JPA(spring-boot-starter-data-jpa 依赖项),它使用 Hibernate 作为 JPA 实现。
我自动配置了我的容器 (@EnableAutoConfiguration) 并且我使用 EntityManager 进行 CRUD 操作。
问题:
我在启动时使用此 EntityManager 通过 HQL 查询加载我的实体,但是当我想编辑或删除其中任何一个时,我收到以下错误
org.springframework.dao.InvalidDataAccessApiUsageException: Removing a detached instance com.phistory.data.model.car.Car#2; nested exception is java.lang.IllegalArgumentException: Removing a detached instance com.phistory.data.model.car.Car#2
org.springframework.dao.InvalidDataAccessApiUsageException: Entity not managed; nested exception is java.lang.IllegalArgumentException: Entity not managed
图书馆:
- spring-boot-starter-data-jpa 1.4.4.RELEASE(Hibernate 5.0.11.Final)
主要:
@SpringBootApplication
@EnableAutoConfiguration
@Slf4j
public class Main {
public static void main(String[] args) {
try {
SpringApplication.run(Main.class, args);
} catch (Exception e) {
log.error(e.toString(), e);
}
}
数据库配置(没有显式声明 beans,EntityManager 自动装配):
@Configuration
@ComponentScan("com.phistory.data.dao")
@EntityScan("com.phistory.data.model")
@EnableTransactionManagement
@PersistenceUnit
public class SqlDatabaseConfig {
}
DAO
@Transactional
@Repository
public class SqlCarDAOImpl extends SqlDAOImpl<Car, Long> implements SqlCarDAO {
@Autowired
public SqlCarDAOImpl(EntityManager entityManager) {
super(entityManager);
}
@Override
public List<Car> getAll() {
return super.getEntityManager()
.createQuery("FROM Car AS car")
.getResultList();
}
}
父 DAO
@Transactional
@Repository
@Slf4j
@NoArgsConstructor
public abstract class SqlDAOImpl<TYPE extends GenericEntity, IDENTIFIER> implements SqlDAO<TYPE, IDENTIFIER> {
@Getter
@PersistenceContext
private EntityManager entityManager;
public SqlDAOImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
public void saveOrEdit(TYPE entity) {
if (entity != null) {
if (entity.getId() == null) {
log.info("Saving new entity: " + entity.toString());
this.entityManager.persist(entity);
} else {
log.info("Editing entity: " + entity.toString());
this.entityManager.refresh(entity);
}
}
}
public void delete(TYPE entity) {
if (entity != null) {
log.info("Deleting entity: " + entity.toString());
this.entityManager.remove(entity);
}
}
public Session getCurrentSession() {
return this.entityManager.unwrap(Session.class);
}
}
为什么我加载的实体没有附加到会话中?保存新实体显然可以正常工作,因为此时不得管理该实体。
非常感谢
问候
在dao外部调用dao的方法时,实体是分离的。
要保持会话,@Transactional 注释应该在调用您的 dao 方法的方法上定义。
例如)
public class CarService {
@Transactional <-- {here}
public void doSomething(){
Car car = sqlDao.getOne(1);
sqlDao.update(car);
sqlDao.delete(car);
}
}
如果您确实想使用 Spring Data JPA,为什么要直接使用 EntityManager?
您上面发布的所有内容都可用 "out of the box":
应用程序启动器:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
Spring 数据 JPA 存储库:
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.example.model.CollectionParent;
import java.lang.String;
import java.util.List;
@Repository
public interface CarRepository extends CrudRepository<Car, Long> {
// nearly everything you need comes for free
}
CarRepository 现在为您提供开箱即用的方法 findAll() : Iterable<Car>
和 delete(Car car)
。
DAO/服务可能如下所示:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class CarService {
@Autowired
private CarRepository carRepository;
@Transactional
public Iterable<Car> findCars() {
return carRepository.findAll();
}
@Transactional
public void updateCar(final Long carId, ...some other params ...) {
final Car car = carRepository.findOne(carId);
car.setXXX ...use some other params here ...
car.setYYY ...use some other params here ...
}
@Transactional
public void updateCar(final Car detachedAndModifiedCar) {
carRepository.save(detachedAndModifiedCar);
}
...
}
如果您将汽车实体加载到当前的持久性上下文中,修改它并让 Hibernate 在刷新时存储更改,最好在数据库事务中存储,则可以轻松实现更新。这可以通过用 @Transactional
.
注释的 Service / DAO 方法轻松实现
也可以将分离的实体传递给服务,只需重新附加并用 carRepository.save(detachedAndModifiedCar)
保存即可。
即使您的 saveOrEdit 方法只是在现有实体的情况下调用 entityManager.refresh(entity)
。这意味着根本没有更新代码,还是我弄错了?实体将仅使用数据库中的数据进行更新。
我有一个项目使用 Spring Boot JPA(spring-boot-starter-data-jpa 依赖项),它使用 Hibernate 作为 JPA 实现。
我自动配置了我的容器 (@EnableAutoConfiguration) 并且我使用 EntityManager 进行 CRUD 操作。
问题: 我在启动时使用此 EntityManager 通过 HQL 查询加载我的实体,但是当我想编辑或删除其中任何一个时,我收到以下错误
org.springframework.dao.InvalidDataAccessApiUsageException: Removing a detached instance com.phistory.data.model.car.Car#2; nested exception is java.lang.IllegalArgumentException: Removing a detached instance com.phistory.data.model.car.Car#2
org.springframework.dao.InvalidDataAccessApiUsageException: Entity not managed; nested exception is java.lang.IllegalArgumentException: Entity not managed
图书馆:
- spring-boot-starter-data-jpa 1.4.4.RELEASE(Hibernate 5.0.11.Final)
主要:
@SpringBootApplication
@EnableAutoConfiguration
@Slf4j
public class Main {
public static void main(String[] args) {
try {
SpringApplication.run(Main.class, args);
} catch (Exception e) {
log.error(e.toString(), e);
}
}
数据库配置(没有显式声明 beans,EntityManager 自动装配):
@Configuration
@ComponentScan("com.phistory.data.dao")
@EntityScan("com.phistory.data.model")
@EnableTransactionManagement
@PersistenceUnit
public class SqlDatabaseConfig {
}
DAO
@Transactional
@Repository
public class SqlCarDAOImpl extends SqlDAOImpl<Car, Long> implements SqlCarDAO {
@Autowired
public SqlCarDAOImpl(EntityManager entityManager) {
super(entityManager);
}
@Override
public List<Car> getAll() {
return super.getEntityManager()
.createQuery("FROM Car AS car")
.getResultList();
}
}
父 DAO
@Transactional
@Repository
@Slf4j
@NoArgsConstructor
public abstract class SqlDAOImpl<TYPE extends GenericEntity, IDENTIFIER> implements SqlDAO<TYPE, IDENTIFIER> {
@Getter
@PersistenceContext
private EntityManager entityManager;
public SqlDAOImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
public void saveOrEdit(TYPE entity) {
if (entity != null) {
if (entity.getId() == null) {
log.info("Saving new entity: " + entity.toString());
this.entityManager.persist(entity);
} else {
log.info("Editing entity: " + entity.toString());
this.entityManager.refresh(entity);
}
}
}
public void delete(TYPE entity) {
if (entity != null) {
log.info("Deleting entity: " + entity.toString());
this.entityManager.remove(entity);
}
}
public Session getCurrentSession() {
return this.entityManager.unwrap(Session.class);
}
}
为什么我加载的实体没有附加到会话中?保存新实体显然可以正常工作,因为此时不得管理该实体。
非常感谢 问候
在dao外部调用dao的方法时,实体是分离的。 要保持会话,@Transactional 注释应该在调用您的 dao 方法的方法上定义。
例如)
public class CarService {
@Transactional <-- {here}
public void doSomething(){
Car car = sqlDao.getOne(1);
sqlDao.update(car);
sqlDao.delete(car);
}
}
如果您确实想使用 Spring Data JPA,为什么要直接使用 EntityManager?
您上面发布的所有内容都可用 "out of the box":
应用程序启动器:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
Spring 数据 JPA 存储库:
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.example.model.CollectionParent;
import java.lang.String;
import java.util.List;
@Repository
public interface CarRepository extends CrudRepository<Car, Long> {
// nearly everything you need comes for free
}
CarRepository 现在为您提供开箱即用的方法 findAll() : Iterable<Car>
和 delete(Car car)
。
DAO/服务可能如下所示:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class CarService {
@Autowired
private CarRepository carRepository;
@Transactional
public Iterable<Car> findCars() {
return carRepository.findAll();
}
@Transactional
public void updateCar(final Long carId, ...some other params ...) {
final Car car = carRepository.findOne(carId);
car.setXXX ...use some other params here ...
car.setYYY ...use some other params here ...
}
@Transactional
public void updateCar(final Car detachedAndModifiedCar) {
carRepository.save(detachedAndModifiedCar);
}
...
}
如果您将汽车实体加载到当前的持久性上下文中,修改它并让 Hibernate 在刷新时存储更改,最好在数据库事务中存储,则可以轻松实现更新。这可以通过用 @Transactional
.
也可以将分离的实体传递给服务,只需重新附加并用 carRepository.save(detachedAndModifiedCar)
保存即可。
即使您的 saveOrEdit 方法只是在现有实体的情况下调用 entityManager.refresh(entity)
。这意味着根本没有更新代码,还是我弄错了?实体将仅使用数据库中的数据进行更新。