2016-10-25 5 views
3

私はデシベルBrand、次の簡単な構造でProductで二つのテーブルがあります。独立エンティティは春データに固執する渡さ

を|ブランド| ID PK |

|製品| ID PK | brand_id FK |そのテーブル用

と実体:

@Entity 
@Table(name = "Brand") 
public class Brand { 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    private Long id; 

    @Column(name = "brand") 
    private String brand; 

    /* getters and setters */ 
} 

@Entity 
@Table(name = "Product") 
public class Product { 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    private Long id; 

    @ManyToOne(cascade = CascadeType.ALL) 
    @JoinColumn(name = "brand_id") 
    private Brand brand; 

    /* getters and setters */ 
} 

私は春のデータを使用すると、私はブランドの実装とリポジトリとサービスを持っている:

@Repository 
public interface BrandRepository extends JpaRepository<Brand, Long> { 

    Brand findByBrand(String brand); 
} 

public interface BrandService { 

    Brand findByBrand(String brand); 
} 
@Service 
public class BrandServiceImpl implements BrandService { 

    @Autowired 
    private BrandRepository brandRepository; 

    @Override 
    public Brand findByBrand(String brand) { 

     return brandRepository.findByBrand(brand); 
    } 
} 

と製品のための:

@Repository 
public interface ProductRepository extends JpaRepository<Product, Long> { 
} 

public interface ProductService { 

    Product save(Product product); 
} 

@Service 
public class ProductServiceImpl implements ProductService { 

    @Autowired 
    private ProductRepository productRepository; 

    @Override 
    public Product save(Product product) { 
     return productRepository.save(product); 
    } 
} 

目標は、Productオブジェクトを保存することです。指定された新しい名称でブランド物が私のDBにない場合には正常に動作します

Brand brand = brandService.findByBrand(brandName); 
if (brand == null) { 
    brand = new Brand(); 
    brand.setBrand("Some name"); 
} 
product.setBrand(brand); 
productService.save(product); 

:それはDBに存在しないか、またはそれ以外の製品に設定する必要がある場合ブランドオブジェクトが自動的に保存されなければなりません。しかし、それが私の場合:

PersistentObjectException: detached entity passed to persist 

ブランド:

カスケードタイプをMERGEに変更できますが、正常に動作します。私は指定された新しい名称とMERGEカスケードタイプとブランドオブジェクトを使用してコードを実行した場合しかし、私は(それは本当に驚いていないのです)ブランドのための

IllegalStateException: 
org.hibernate.TransientPropertyValueException: 
object references an unsaved transient instance - save the transient instance before flushing 

を取得し、私のデシベルではありません。

どのようなカスケードタイプが必要ですか?私が間違っていたのOt?

+1

ほとんどの最適化クエリ(少ない休止SQLクエリ)を伴って含む1のPersistenceContextに全体の議論をもたらします。製品はどこから来ますか?製品を構築して保存するクラス全体を投稿します。 –

答えて

5

短い答え:

あなたのカスケードの注釈には問題はありません。自動カスケードに頼るべきではなく、このロジックを手作業で、そしてサービスレイヤー内で実装するべきです。

長い答え:

あなたは、2つのシナリオがあります。

  • シナリオ1 - CascadeType - エンティティ が
  • シナリオ2を永続化するために渡さ外しCascadeType.ALL +既存のブランドを=。+新ブランドをMERGE =

シナリオ1をFLUSHIN前 に一時的なインスタンスを保存JPAはPRODUCT(CascadeType.ALL)を持続した後BRANDを持続しようとしているので起こります。 BRANDが既に存在すると、エラーが発生します。

JPAがBRAND(CascadeType.MERGE)を保持しようとしておらず、以前にBRANDが永続化されていなかったため、シナリオ2が発生しました。

非常に多くの抽象レイヤが存在するため、解決策を見つけるのは難しいです。 Springデータは、JDBCを抽象化するHibernateを抽象化するJPAを抽象化します。

可能な解決策は、CascadeType.MERGEが機能するように、EntityManager.persistの代わりにEntityManager.mergeを使用することです。私は、Spring Data Saveメソッドを再実装することができると信じています。ここにそれに関する参考文献がいくつかあります:Spring Data: Override save method

もう一つの解答は短い答えでしょう。

例:メソッドに@Transactionalを追加

@Override 
public Product save(Product product, String brandName) { 

    Brand brand = brandService.findByBrand(brandName); 
    if (brand == null) { 
     brand = brandService.save(brandName); 
    } 
    return productRepository.save(product); 

} 
0

が欠落により、取引に

@Override 
@Transactional 
public Product save(Product product, String brandName) { 

    Brand brand = brandService.findByBrand(brandName); 
    if (brand == null) { 
     brand = brandService.save(brandName); 
    } 
    return productRepository.save(product); 

} 
関連する問題