JPA Eclipselink JOIN FETCH LAZY关系返回null

时间:2014-03-26 22:20:08

标签: jpa null eclipselink lazy-loading fetch

我总是从JPA Query中的JOIN FETCH子句获取NULL,即使我按预期配置了所有内容:

@XmlRootElement
@XmlAccessorType(XmlAccessType.PROPERTY)
@Entity
@Table(name = "TB_BANNER_IMAGE")
public class BannerImage extends BaseEntity<Integer> {      
    protected FileReference fileReference;
    private String type;
    private String labelTitle;  

    protected BannerImage() {}

    @Id    
    @TableGenerator(name="genBannerImage", table="TB_ID_GENERATOR",
            pkColumnName="ID_NAME", valueColumnName="ID_VAL",
            pkColumnValue="TB_BANNER_IMAGE", allocationSize=1)
    @GeneratedValue(strategy=GenerationType.TABLE, generator="genBannerImage")
    @Column(name = "ID_BANNER_IMAGE", unique = true, nullable = false)
    public Integer getId() {
        return super.getId();
    }

    @Override
    public void setId(Integer id) {
        super.setId(id);
    }

    @Column(name="TYPE")
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @OneToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
    @JoinColumn(name="ID_FILE_REFERENCE", nullable=false)
    public FileReference getFileReference() {
        return fileReference;
    }

    public void setFileReference(FileReference fileReference) {
        this.fileReference = fileReference;
    }

    @Column(name="LABEL_TITLE")
    public String getLabelTitle() {
        return labelTitle;
    }

    public void setLabelTitle(String labelTitle) {
        this.labelTitle = labelTitle;
    }
}

for File Reference Class:

@Entity
@Table(name = "TB_FILE_REFERENCE")
public class FileReference extends BaseNamedEntity<String> {

    private String type;

    public FileReference() {}

    @Id    
    @TableGenerator(name="genFileReference", table="TB_ID_GENERATOR",
            pkColumnName="ID_NAME", valueColumnName="ID_VAL",
            pkColumnValue="TB_FILE_REFERENCE", allocationSize=1)
    @GeneratedValue(strategy=GenerationType.TABLE, generator="genFileReference")
    @Column(name = "ID_FILE_REFERENCE", unique = true, nullable = false)
    public String getId() {
        return super.getId();
    }
    @Override
    public void setId(String id) {
        super.setId(id);
    }

    @Column(name = "TYPE")
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }       
}

服务类:

@Path("/banner")
public class BannerImageService extends BaseServiceFacade<BannerImage, Integer> {
        @SuppressWarnings("unchecked")
    @Override
    public Crud<BannerImage, Integer> lookupService() throws ServiceLocatorException {          
        return ServiceLocator.getInstance()
                .getLocalHome(ServicesConstants.BANNER_IMAGE_SERVICE);      
    }

    @Override
    protected String getDefaultGetQuery() {
        return BannerImageDAO.GET_BY_ID_FETCH_FILE_REF;
    }

    @Override
    protected String getDefaultQuery() {
        return BannerImageDAO.GET_ALL_FETCH_FILE_REF;
    }    
}

获取BaseServiceFacade的REST方法:

@Override
@GET
@Consumes(MediaType.APPLICATION_JSON)
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Path("/{id}")
public T get(@PathParam("id") ID id) {
    try {
        if (!validateID(id)) {
            logMessage("Invalid Entity ID: " + id);
            return null;    
        }           

        String defaultGetQuery = getDefaultGetQuery();
        if (defaultGetQuery != null) {
            Map<String, Object> mapParams = new HashMap<String, Object>();
            mapParams.put("id", id);
            List<T> entityList = getService().search(defaultGetQuery, mapParams);
            if (entityList != null && entityList.size() == 1) {
                T ent = entityList.get(0);
                return ent;
            } else {
                logMessage("Invalid search by Entity ID: " + id);
            }

        } else {                
            return getService().findById(clazz, id);    
        }           

} catch (ServiceException e) {
    serviceException(e);
} catch (Exception ex) {
    logException(ex);
}
    return null;
}

最后是从entityManager读取的Service Bean EJB:

public class BaseServiceBean<T extends IEntity<ID>, ID extends Serializable> implements Crud<T,ID> {

        // ... generic methods to be reused by subclasses

        @Override
        public List<T> search(String queryOrNamedQuery) throws ServiceException {
            return search(queryOrNamedQuery, null, 0, 0);
        }

        @SuppressWarnings("unchecked")
        public List<T> search(String namedQueryOrHql, Map<String, Object> parameters, int start, int chunkSize) {
            try {
                Query query = createQuery(namedQueryOrHql, getQueryType(namedQueryOrHql));
                if (start > 0) {
                    query.setFirstResult(start);
                }
                if (chunkSize > 0) {
                    query.setMaxResults(chunkSize);
                }
                addParameters(query, parameters);
                List<T> result = query.getResultList();
                afterSearch(result); 
                return result;

            } catch (NoResultException nre) {
                nre.printStackTrace();
            } catch (ClassCastException cce) {
                cce.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();            
            }
            return null;
        }

    protected void afterSearch(List<T> result) {

    }
// etc...

实现BannerImageService的特定类:

    @Stateless(mappedName="ejb/BannerImageService")
    public class BannerImageServiceBean extends BaseServiceBean<BannerImage, Integer> implements BannerImageServiceBeanRemote, BannerImageServiceBeanLocal {

    @Override
    protected void afterSearch(List<BannerImage> result) {

        if (result != null && result.size() == 1) {         
            BannerImage bannerImage = result.get(0);
            bannerImage.getFileReference();
        }

        super.afterSearch(result);
    }

// additional code ...

当我尝试与它的相应FileReference成员一起获取我的BannerImage类时,即使在我的数据库中存在一个现有的外键,我总是得到NULL:

JPQL:

"SELECT a FROM BannerImage a join fetch a.fileReference WHERE a.id = :id";

生成的SQL:

SELECT t1.ID_BANNER_IMAGE, t1.LABEL_TEXT, t1.LABEL_TITLE, t1.TYPE, 
t1.ID_FILE_REFERENCE, t0.ID_FILE_REFERENCE, t0.NAME, 
t0.TYPE FROM TB_FILE_REFERENCE t0, TB_BANNER_IMAGE 
t1 WHERE (t0.ID_FILE_REFERENCE = t1.ID_FILE_REFERENCE) AND t1.ID_BANNER_IMAGE = 1

在我的数据库中,记录显示正确的参考:

BANNER_IMAGE:

1;"";"main";"2bdbb063d0d0ee2939c89763945d9d9e";"banner1.png";"image/png"

如果我执行:

select * from TB_FILE_REFERENCE where ID_FILE_REFERENCE = '2bdbb063d0d0ee2939c89763945d9d9e'

我可以在数据库中找到记录,尽管我的EclipseLink JPA实现总是返回null:

enter image description here

EclipseLink版本2.5.2-M1

这是实体从服务层传递到

的方式

有人可以帮助指出JOIN FETCH无法正常工作的原因吗?

1 个答案:

答案 0 :(得分:0)

我遇到了类似的问题,仔细观察我发现这个问题只发生在最近创建/保存的实体上。然后我认为它与eclipselink缓存有关。我通过在进行连接获取JPQL查询之前添加此行来解决此问题,

em.getEntityManagerFactory().getCache().evictAll();
em.createQuery("SELECT a FROM BannerImage a join fetch a.fileReference WHERE a.id = :id").getResultList();

HTH!