【发布时间】:2014-03-26 22:20:07
【问题描述】:
我总是从我的 JPA 查询中的 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;
}
}
对于文件引用类:
@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 ...
当我尝试获取 BannerImage 类及其对应的 FileReference 成员时,我总是得到 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:
EclipseLink 版本 2.5.2-M1
这就是实体如何从服务层传递到
有人可以帮忙指出为什么 JOIN FETCH 不能正常工作吗?
【问题讨论】:
-
检查如果你使用访问器方法而不是在调试器中检查字段会发生什么。编织惰性关系意味着该字段在被访问之前不会被填充。您可以通过检查访问惰性关系时发出的 SQL 来检查 fetch 连接是否有效 - 如果发出 SQL 来查询它,则说明有问题。
-
每当我尝试执行 getFileReference() 时,我都会得到:[EclipseLink-7242](Eclipse Persistence Services - 2.5.0.v20130507-3faac2b):org.eclipse.persistence.exceptions.ValidationException 异常说明: 尝试使用具有空会话的间接遍历关系。这通常发生在具有未实例化 LAZY 关系的实体被序列化并且在序列化之后遍历该惰性关系时。为避免此问题,请在序列化之前实例化 LAZY 关系
-
这意味着实体已被序列化,因此工厂不再可用于读取关系。您使用的是什么版本的 Eclipselink,您能否显示您用于在实体中读取的代码?您之前是否阅读过实体,或者这是一个明确的实体管理器上下文?
-
更新了添加上述信息的问题。我从 EJB 读取实体并返回 List
result = query.getResultList(); 的结果 -
afterSearch 是做什么的?如果您只是执行一个简单的 JPQL “SELECT a FROM BannerImage a join fetch a.fileReference WHERE a.id = :id”并立即检查 fileReference,它是否被提取?我能看到这种情况发生的唯一方法是,如果刷新返回的 BannerImage,这将重置间接/惰性关系
标签: jpa null eclipselink lazy-loading fetch