我正在努力解决在 JSF 验证阶段发生的问题,不知何故,验证正在做一些休眠不喜欢的事情(未能懒惰地初始化一个集合,没有会话或会话被关闭),我想了解什么.
上下文是这样的:我有一张支票,它以多对多的关系链接到 CheckType。我正在编辑现有检查(在该编辑页面中提供了许多组合框,以便我将其链接到特定检查类型)。
我也在使用 Primefaces。
问题发生在验证过程结束时,但我的转换器工作正常(返回正确的结果)。
此外,我真正不明白的是为什么会有一个 lazily,而所有这些对象都以 EAGER 模式加载。
感谢您的洞察力,我对休眠和将其链接到 JSF 也很陌生:/
堆栈跟踪
11:15:08,137 INFO [com.bdls.ids.utils.BDPhaseListener] (http-localhost-127.0.0.1-8080-3) Before PROCESS_VALIDATIONS 3
11:15:11,439 INFO [stdout] (http-localhost-127.0.0.1-8080-3) Hibernate:
11:15:11,440 INFO [stdout] (http-localhost-127.0.0.1-8080-3) select
11:15:11,486 INFO [stdout] (http-localhost-127.0.0.1-8080-3) Hibernate:
11:15:11,486 INFO [stdout] (http-localhost-127.0.0.1-8080-3) select
11:15:11,503 INFO [stdout] (http-localhost-127.0.0.1-8080-3) Hibernate:
11:15:11,504 INFO [stdout] (http-localhost-127.0.0.1-8080-3) select
11:15:12,942 INFO [stdout] (http-localhost-127.0.0.1-8080-3) Hibernate:
11:15:12,944 INFO [stdout] (http-localhost-127.0.0.1-8080-3) select
11:15:13,023 INFO [stdout] (http-localhost-127.0.0.1-8080-3) Hibernate:
11:15:13,023 INFO [stdout] (http-localhost-127.0.0.1-8080-3) select
11:15:13,040 INFO [stdout] (http-localhost-127.0.0.1-8080-3) Hibernate:
11:15:13,062 INFO [stdout] (http-localhost-127.0.0.1-8080-3) Hibernate:
11:15:13,856 WARNING [javax.enterprise.resource.webcontainer.jsf.lifecycle] (http-localhost-127.0.0.1-8080-3) failed to lazily initialize a collection, no session or session was closed: org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:393) [hibernate-core-4.0.1.Final.jar:4.0.1.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:385) [hibernate-core-4.0.1.Final.jar:4.0.1.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:378) [hibernate-core-4.0.1.Final.jar:4.0.1.Final]
at org.hibernate.collection.internal.PersistentSet.add(PersistentSet.java:206) [hibernate-core-4.0.1.Final.jar:4.0.1.Final]
at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValuesForModel(MenuRenderer.java:382) [jsf-impl-2.1.7-jbossorg-2.jar:]
at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectManyValue(MenuRenderer.java:129) [jsf-impl-2.1.7-jbossorg-2.jar:]
at com.sun.faces.renderkit.html_basic.MenuRenderer.getConvertedValue(MenuRenderer.java:315) [jsf-impl-2.1.7-jbossorg-2.jar:]
at org.primefaces.component.selectmanycheckbox.SelectManyCheckboxRenderer.getConvertedValue(SelectManyCheckboxRenderer.java:36) [primefaces-3.4.2.jar:]
at javax.faces.component.UIInput.getConvertedValue(UIInput.java:1030) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.UIInput.validate(UIInput.java:960) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.UIInput.executeValidate(UIInput.java:1233) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.UIInput.processValidators(UIInput.java:698) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.UIForm.processValidators(UIForm.java:253) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1172) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:76) [jsf-impl-2.1.7-jbossorg-2.jar:]
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) [jsf-impl-2.1.7-jbossorg-2.jar:]
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118) [jsf-impl-2.1.7-jbossorg-2.jar:]
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:79) [primefaces-3.4.2.jar:]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
at com.bdls.ids.controller.login.NoCacheFilter.doFilter(NoCacheFilter.java:39) [classes:]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
at com.bdls.ids.controller.login.AuthenticationFilter.doFilter(AuthenticationFilter.java:48) [classes:]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161) [jbossweb-7.0.13.Final.jar:]
at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.1.1.Final.jar:7.1.1.Final]
at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368) [jbossweb-7.0.13.Final.jar:]
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [jbossweb-7.0.13.Final.jar:]
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671) [jbossweb-7.0.13.Final.jar:]
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930) [jbossweb-7.0.13.Final.jar:]
at java.lang.Thread.run(Unknown Source) [rt.jar:1.6.0_32]
11:15:13,901 INFO [com.bdls.ids.utils.BDPhaseListener] (http-localhost-127.0.0.1-8080-3) After PROCESS_VALIDATIONS 3
为了简洁起见,我只放了有趣的部分:
Xhtml
<h:form id="new-items" prependId="false">
<label for="typechecks">Type of checks :</label>
<p:selectManyCheckbox id="typechecks" value="#{checksController.itemEdited.checkTypes}" layout="pageDirection" converter="checkTypeConverter">
<f:selectItems value="#{checkTypeController.allItems}" var="checkType" itemLabel="#{checkType.name}" />
</p:selectManyCheckbox>
<p:commandButton id="save" value="Save" action="#{checksController.persistItemEdited}" styleClass="btnFooterFormNewItems" icon="ui-icon-check" update="new-items" />
</h:form>
转换器
@FacesConverter("checkTypeConverter")
public class CheckTypeConverter implements Converter {
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null || value.toString().isEmpty()) {
return "";
}
return ((UnversionedObject) value).getId().toString();
}
public Object getAsObject(FacesContext context, UIComponent arg1, String value) {
CheckTypeService checkTypeService = FacesContextUtils.getWebApplicationContext(context).getBean("checkTypeService",
CheckTypeService.class);
return checkTypeService.getById(Integer.parseInt(value));
}
}
控制器
@ManagedBean
@ViewScoped
public class ChecksController implements Serializable {
private static Logger log = Logger.getLogger(ChecksController.class);
// -------------------------------------------------
// properties
// -------------------------------------------------
@ManagedProperty(value = "#{checksService}")
private ChecksService checksService;
// -------------------------------------------------
// parameters
// -------------------------------------------------
private List<Check> allChecks;
private Check[] selectedItem;
private Check itemEdited = new Check();
// -------------------------------------------------
// constructor
// -------------------------------------------------
public ChecksController() {
log.info("Creation of checks controller");
allChecks = new ArrayList<Check>();
}
// -------------------------------------------------
// methods
// -------------------------------------------------
@PostConstruct
private void load() {
if(FacesContext.getCurrentInstance().isPostback()) return;
log.info("loading list of checks...");
allChecks = checksService.getAll();
log.info(String.format("Loaded %s items", allChecks.size()));
}
public void delete(Check c)
{
try{
checksService.delete(c);
}catch(Exception e){
log.error("Error when deleting object "+c);
e.printStackTrace();
}
}
public void prefillFrom(String modelID) {
log.info("Pre-filling based on model " + modelID);
if (modelID == null) // new item
return;
// in this case, the item must exist (edition)
itemEdited = checksService.getById(Integer.parseInt(modelID));
if (itemEdited == null) {
UINotification.sendAsError("Item does not exist", String.format("The item %s does not exist", modelID));
log.error(String.format("The item %s does not exist", modelID));
}
}
public void persistItemEdited(){
log.info("persisting item edited...");
// CheckType c = new CheckType();
// c.setName("jolitest");
//
// itemEdited.getCheckTypes().add(c);
// c.getChecks().add(itemEdited);
//
try{
checksService.saveOrUpdate(itemEdited);
}catch(Exception e){
log.error("Error when saving object "+itemEdited);
e.printStackTrace();
}
itemEdited = new Check() ;
}
public void test()
{
CheckType ct = new CheckType();
ct.setName("toto");
itemEdited.getCheckTypes().add(ct);
Set<CheckType> set = new HashSet<CheckType>();
set.add(ct);
itemEdited.setCheckTypes(set);
}
getters and setters...
服务
@Service("checksService")
@Transactional(readOnly = true)
public class ChecksServiceImpl implements ChecksService {
@Autowired
CheckDAO checkDAO;
@Autowired
CheckTypeDAO checkTypeDAO;
/**
* retrieves a UnversionedObject based on its id
*/
public Check getById(Integer id) {
return checkDAO.getById(id);
}
@Transactional(readOnly = false)
public Check saveOrUpdate(Check c) {
//for many-to-many relations, re-attach the entities
if (c.getCheckTypes() != null && c.getCheckTypes().size() > 0)
{
List<CheckType> checkTypes = new ArrayList<CheckType>(c.getCheckTypes().size());
for (CheckType ct : c.getCheckTypes()) {
checkTypes.add(checkTypeDAO.getById(ct.getId()));
}
c.getCheckTypes().clear();
c.getCheckTypes().addAll(checkTypes);
}
return checkDAO.saveOrUpdateDetached(c);
}
/**
* returns a list of all checks
*/
@Override
public List<Check> getAll() {
List<Check> list = checkDAO.getAll();
return list;
}
/**
* empty the extra relations and deletes the object
*/
@Override
@Transactional(readOnly = false)
public void delete(Check c) {
// attach object
c = checkDAO.getById(c.getId());
// initialize list
Hibernate.initialize(c.getCheckTypes());
// first remove relations to checktype
c.getCheckTypes().clear();
// update
checkDAO.saveOrUpdateDetached(c);
// now delete
checkDAO.delete(c);
}
}
DAO (实现)
@Repository
public class CheckDAOImpl extends BaseDAOimpl<Check> implements CheckDAO {
}
public abstract class BaseDAOimpl<T extends UnversionedObject> implements BaseDAO<T> {
@Override
public T saveOrUpdateDetached(T object) throws DataModelConsistencyException {
updateUserAndTime(object);
if (object.getId() != null) {
return _entityManager.merge(object);
} else {
_entityManager.persist(object);
return object;
}
}
rest omitted
}
查看
@Entity
@Table(name = "IDS_SV_CHECK")
public class Check extends UnversionedObject {
public static enum Severity {
HIGH, MEDIUM, LOW, INFORMATIVE
};
@Column(length = 32)
private String checkName;
@Column
private Integer subCheckID;
@Enumerated(EnumType.STRING)
private Severity severity;
@Column(length = 32)
private String description;
@Column(length = 32)
private String errorMsg;
@Column(length = 32)
private String DMVersion;
@Column(length = 32)
private String therapeuticArea;
@ManyToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
@JoinTable(name = "IDS_SV_CHECK_HAS_TYPE",
joinColumns={@JoinColumn(name="check_id")},
inverseJoinColumns={@JoinColumn(name="checktype_id")})
@BatchSize(size = 100)
private Set<CheckType> checkTypes = new HashSet<CheckType>();
...
检查类型
@Entity
@Table(name = "IDS_SV_CHECKTYPE")
public class CheckType extends UnversionedObject {
@Column(length=60)
private String name;
@ManyToMany(mappedBy="checkTypes",fetch=FetchType.EAGER)
private Set<Check> checks = new HashSet<Check>();