2013-05-24 22 views
8

Tôi gặp sự cố lạ với Spring và Hibernate khi chạy nhiều luồng trong một ứng dụng. Tôi đang sử dụng mùa xuân 3.2.0.RELEASE và hibernate 4.1.12.Final. Vấn đề là đối với một số đối tượng, khi chúng được lấy ra từ db, quá trình truy xuất thành công, nhưng tất cả các bộ sưu tập được ánh xạ sẽ không được thiết lập. Dưới đây là một ví dụ về repo của tôi:NullPointerException khi truy cập bộ sưu tập tải lười trong phương thức DAO

@Repository("fooRepository") 
public class FooRepository { 

    private static final Logger log = Logger.getLogger(FooRepository.class); 

    @Autowired 
    private SessionFactory sessionFactory; 

    @Override 
    @Transactional 
    public Foo retrieve(final Long id) { 
     Foo instance = (Foo) sessionFactory.getCurrentSession().get(Foo.class, id); 
     for (CollectionMember member : instance.getCollectionMembers()) { 
      log.debug(member.getId());  
     } 
     return instance; 
    } 

Đối với một số đối tượng, phương pháp này luôn luôn ném lỗi sau khi cố gắng truy cập vào danh sách CollectionMember:

Caused by: java.lang.NullPointerException 
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:501) 

Vấn đề là các session.get() cuộc gọi tạo ra các đối tượng PersistentBag cho tất cả các bộ sưu tập được tải chậm nhưng không bao giờ đặt nội dung. Điều này chỉ xảy ra khi đa luồng được bật. Ai có thể giải thích tại sao điều này xảy ra?

EDIT: Dưới đây là các bit tương ứng của lớp foo:

@Entity 
@Table(name = "FOO") 
@XmlRootElement(name = "foo") 
public class Foo { 

    @EmbeddedId 
    private FooPK id; 

    @OneToMany 
    @NotFound(action = NotFoundAction.IGNORE) 
    @JoinColumns({ 
      @JoinColumn(name = "COLLECTION_ID", referencedColumnName = "COLLECTION_ID"), 
      @JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID")}) 
    private List<CollectionMember> collectionMembers = new ArrayList<CollectionMember>(); 

Và lớp CollectionMember:

@Entity 
@Table(name = "COLLECTION_MEMBER") 
@XmlRootElement(name = "collectionMember") 
public class CollectionMember { 

    @EmbeddedId 
    private CollectionMemberPK primaryKey; 

    @ManyToOne 
    @JoinColumn(name = "COLL_CODE") 
    private CollectionCode collectionCode; 
+3

Cung cấp dấu vết ngăn xếp đầy đủ (ngay cả khi nó tiết lộ một chút) có thể giúp khắc phục sự cố tốt hơn. – Subhas

+0

bất kỳ cơ hội nào bạn có thể đăng các lớp Foo và CollectionMember, hoặc ít nhất các bit hiển thị chú thích ánh xạ (hoặc hbm.xml nếu bạn đang sử dụng cấu hình xml) –

+0

Tôi không thể đăng stacktrace đầy đủ trừ khi tôi đi qua và thay đổi theo cách thủ công một loạt các tên gói để bạn không thể biết nó đến từ đâu. Nếu bạn thực sự nghĩ rằng stacktrace đầy đủ sẽ cung cấp thêm thông tin tôi sẽ làm điều đó, nhưng tôi không thuyết phục bạn cần bất kỳ nhiều hơn bạn đã có trong ý nghĩa đó, tôi sẽ thêm ánh xạ mặc dù. – Rockenstein

Trả lời

0

Trường hợp không xử lý đa luồng đi vào chơi? Làm thế nào để đảm bảo rằng một sợi đơn luôn luôn sử dụng cùng một phiên ngủ đông?

Cách tôi hiểu tải ngủ đông và lười biếng, khi bạn duyệt bộ sưu tập Hibernate sẽ tìm trong CurrentSession. Trong môi trường đa luồng, phiên đó có thể đã được một chuỗi khác sử dụng. Tôi không chắc chắn nếu @Transactional là đủ để bảo tồn cùng một phiên Hibernate của bạn.

Có thể có cấu hình Hibernate cho việc này.

0

Hãy thử cách sau.

Foo instance = (Foo) sessionFactory.getCurrentSession().get(Foo.class, id); 

Hibernate.initialize(instance.getCollectionMembers()) 

for (CollectionMember member : instance.getCollectionMembers()) { 

     log.debug(member.getId());  
} 

return instance; 
0

Hibernate đánh dấu tất cả * ToMany collection ánh xạ như lười theo mặc định, trừ khi có quy định khác. Không thể cho một chủ đề tải đối tượng và không thể đọc bộ sưu tập lười liên quan bên trong một phương thức @Transactional.

Đa luồng trong trường hợp của bạn là gì? Bạn có tạo ra các luồng theo cách thủ công hay là ứng dụng web? Làm thế nào để bạn thiết lập giới hạn phiên?

BTW, nếu bạn thực sự muốn giải quyết vấn đề, tôi khuyên bạn nên đăng stacktrace .. nó có thể mất vài giây để thay thế cho bạn tên gói thương mại, trong trường hợp đó là mối quan tâm.

0

Khi sử dụng ngủ đông trong môi trường mùa xuân, không nên lấy phiên trực tiếp qua nhà máy phiên, có thể dẫn đến rò rỉ phiên. Cố gắng thay thế bằng mô hình sau:

getHibernateTemplate().execute(new HibernateCallback() { 
     public Object doInHibernate(Session session) throws HibernateException, SQLException { 
      Foo instance = (Foo) session.get(Foo.class, id); 
      //whatever logic goes here 
      return instance; 
     } 
    }); 
2

Vấn đề là @NotFound(action = NotFoundAction.IGNORE)

này sẽ tạo ra một null đối tượng quay trở lại thay vì một bộ sưu tập sản phẩm nào trong trường hợp không có hồ sơ thu thập được tìm thấy. Xóa và thử nghiệm nó

2

Tôi nghĩ bạn nên sử dụng bảng tham gia thứ ba như và sử dụng Tiêu chí để tải bộ sưu tập.

@OneToMany(fetch = FetchType.LAZY) 
    @JoinTable(name = "table", joinColumns = {@JoinColumn(name = "COLLECTION_ID", nullable = false)}, inverseJoinColumns = { @JoinColumn(name = "FOO_ID", nullable = true) }) 
    private List<CollectionMember> collectionMembers = new ArrayList<CollectionMember>(); 


@ManyToOne(mappedBy="collectionMembers") 
private CollectionCode collectionCode;