Question

I'm using JBoss 5 with Hibernate.

In single session, I'm creating new Entity object than execute EntityManager.persist(). After (before executing flush()) I could find it by PK using method EntityManager.find(), and I see the the object exists in cache (EntityManager.contains(o) returns true), but I could not load it by executing namedQuery or query..

Why?

/* I have a Entity "DocumentsHistory" and "Documents" they are related (DocumentsHistory contains docId refers to Documents.docId (PK) and Documents contains lastDhId refers to DocumentsHistory.docHistId) */
// I'm working with example of Documents - doc
// I need to create new example of DocumentsHistory and than later in the same transaction select it 

// Creation of new DocumentsHistory

DocumentsHistory dh = new DocumentsHistory();
dh.setDocId(doc.getDocId); // Link with current example of Documents
dh.setType("REFF"); // set String value
dh.setRefId( id );  // set Long value

em.persit(dh); // persist new object

doc.setLastDhId(dh.docHistId);
em.merge(doc); // persist existing object

//... than later in the same transaction I'm trying to execute named Query 

DocumentsHistory dh = (DocumentsHistory)    em.createNamedQuery("DocumentsHistory.findByDocIdType")
                                .setParameter("docId", doc.getDocId())
                                .setParameter("type", "REFF")
                                .getSingleResult();

// and it throws NoResultException
// but:

em.find( DocumentsHistory.class, doc.getLastDhId() ); // returns object

========================================================================================= I also create a simple JPA test (without JBoss) and it works

em.getTransaction().begin();

printAll( Participants.class, em );// prints the whole table

Participants part = new Participants();
part.setCode( "PPPPPPPPPPPP" );
em.persist( part );
printAll( Participants.class, em );  
// FIND new 
Participants p = (Participants)em.createNamedQuery("Participants.findByParticipantCode" ).setParameter( "participantCode", "PPPPPPPPPPPP" ).getSingleResult(); 

System.out.println( "NEW PARTICIPANT: " + p.toString() ); // Prints new object

em.getTransaction().rollback();

@Entity
@Table(name = "PARTICIPANTS")
@NamedQuery(name = "Participants.findByParticipantCode", query = "SELECT p FROM Participants p WHERE p.code = :participantCode ")
public class Participants implements Serializable {

    /**
     * 
     */
    @Id
    @GeneratedValue(generator="VID")
    @SequenceGenerator(name="VID",sequenceName="VID")
    @Column(name = "PARTICIPANT_ID", nullable = false)
    private Long participantId;

        @Column(name = "PARTICIPANT_CODE", nullable = false)
        private String code;

    public Long getParticipantId() {
        return participantId;
    }

    public void setParticipantId(Long participantId) {
        this.participantId = participantId;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }


    @Override
    public String toString() {
        return "ParticId: " + this.participantId + "; code: " + this.code + ";
    }
}
Était-ce utile?

La solution

EntityManager checks the current persistence context (as a kind of first level cache) before going to the database. When you call persist() you're adding to that context.

Here's the find() javadoc (emphasis mine):

Find by primary key. Search for an entity of the specified class and primary key. If the entity instance is contained in the persistence context, it is returned from there.

and contains():

Check if the instance is a managed entity instance belonging to the current persistence context.

In contrast, your named query must access the database, which at this point knows nothing about your cached entity. As you noticed you need to sync your cache with the database by calling flush().

Autres conseils

The 1st and 2nd level cache of Hibernate stores the objects by their identifiers. For this reason the cache is not really usable for queries in a general way. First Hibernate always need to execute your query on the database to retrieve the ids of the matching entities. The result of this query (at this point) is just a list of ids! Only after this Hibernate is able to use the cache and lookup the objects by their ids. With no flush being called your query returns no ids so the cache is not even touched.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top