Spring启动JPA没有将实体附加到会话

时间:2017-03-18 20:07:31

标签: hibernate jpa spring-boot hql

我有一个使用Spring Boot JPA(spring-boot-starter-data-jpa依赖)的项目,它使用Hibernate作为JPA实现。

我自动配置了容器(@EnableAutoConfiguration),我使用EntityManager进行CRUD操作。

问题: 我使用此EntityManager通过HQL查询在启动时加载我的实体,但是当我想编辑或删除其中任何一个时,我收到以下错误

  

org.springframework.dao.InvalidDataAccessApiUsageException:删除分离的实例com.phistory.data.model.car.Car#2;嵌套异常是java.lang.IllegalArgumentException:删除分离的实例com.phistory.data.model.car.Car#2

     

org.springframework.dao.InvalidDataAccessApiUsageException:未管理的实体;嵌套异常是java.lang.IllegalArgumentException:实体未托管

库:

  • 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);
        }
    }

数据库配置(没有显式声明bean,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);
    }
}

为什么我加载的实体没有附加到Session?保存新实体显然工作正常,因为此时不得管理实体。

非常感谢 问候

2 个答案:

答案 0 :(得分:2)

如果您真的想使用Spring Data JPA,为什么直接使用EntityManager?

您在上面发布的所有内容均可“开箱即用”:

应用程序入门:

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 Data 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);
    }

    ...
}

如果您将Car Entity加载到当前的持久性上下文中,修改它并让Hibernate将更改存储在刷新时间,最好是在数据库事务中更新。使用@Transactional注释的Service / DAO方法可以轻松实现这一点。

分离的实体也可以传递给服务,只需重新附加并使用carRepository.save(detachedAndModifiedCar)保存。

即使你的saveOrEdit方法只是在现有实体的情况下调用entityManager.refresh(entity)。这意味着根本没有更新代码,或者我弄错了?实体将仅使用数据库中的数据进行更新。

答案 1 :(得分:1)

当您在dao之外调用dao的方法时,实体将被分离。 为了保持会话,应该在调用你的dao方法的方法上定义@Transactional注释。

例如)

public class CarService {

    @Transactional   <-- {here}
    public void doSomething(){
        Car car = sqlDao.getOne(1);
        sqlDao.update(car);
        sqlDao.delete(car);
    }
}