2017-03-18 17 views
1

JPA実装としてHibernateを使用するSpringブートJPA(spring-boot-startter-data-jpa依存性)を使用するプロジェクトがあります。SpringブートJPAはセッションにエンティティをアタッチしません

私はコンテナを自動的に設定しました(@EnableAutoConfiguration)、私はCRUD操作にEntityManagerを使用します。

問題: 私はHQLを経由して、起動時に私のエンティティをロードするには、このEntityManagerを使用してクエリが、私はそれらのいずれかを編集したり削除したいとき、私は次のようなエラーに

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がある:

  • 春ブート・スターター・データ-JPA 1.4.4.RELEASE(休止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); 
     } 
    } 

データベースの設定(ない豆は明示的に宣言されていないが、EntityManagerが自動的にauの取得しますtowired):

@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のメソッドを呼び出すと、多く 挨拶

答えて

2

あなたが実際に春データ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); 
    } 

} 

春データ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)が呼び出されます。つまり、更新コードがまったくないのですか、それとも間違っていましたか?エンティティはデータベースからのデータで更新されます。

+0

プレミアムなもの!私は@Repositoryがそれを書くことなくCRUDロジックを注入するのに使用できることを知らなかった、これは実際には私がSpring Bootを使用するようにアップグレードした古いプロジェクトだが、出てきたすべての新機能長年にわたって。私はこの方法ですべてのDAOを書き直します、ありがとう! – GCarbajosa

+0

私はあなたを助けることができてうれしいです! :) –

1

おかげで、エンティティが分離されます。 セッションを維持するには、@ TransactionalアノテーションをDAOメソッドを呼び出すメソッドで定義する必要があります。例えば

public class CarService { 

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

私は@Transactionalのエンティティのエディションと、サーバーの起動時にgetAll()メソッドを呼び出すサービスを扱うよりもコントローラとメソッドに注釈を付けることを試みましたが、問題は残ります。私が気づいた1つの変更は、以前のものの後のこの2番目のトレースです。 org.springframework.transaction.TransactionSystemException:できませんでした JPAトランザクションをコミットします。入れ子になった例外は です。javax.persistence.RollbackException: とマークされたトランザクションrollbackOnly]根本原因 – GCarbajosa

関連する問題