我在用版本转换实体时遇到问题。我举了一个简单的例子来解释我的问题,因为“真正”的应用程序太大了,包含了很多不必要的东西。
情境:我有一个带有primefaces和openjpa的web应用程序。我有20个组件(autocompletes selectedmenues)需要一个转换器,它们使用持久性实体。
信息:我只想使用jsf,primefaces!(没有什么比omnifaces或其他东西更特别的了。)问题就在下面。这只是测试代码。它还不完整,还有一些奇怪的事情。但这充其量只能解释我的问题。
实体示例:(仅字段和哈希代码相等)
@Entity
public class Person {
@Id
private Long id;
private String name;
@Version
private Long version;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
第一个解决方案:我的第一个解决方案是为每个组件创建一个自己的转换器。我在那里注入我的托管bean并使用组件“值”中的getter。
豆
@ManagedBean(name = "personBean")
@ViewScoped
public class PersonBean implements Serializable {
private List<Person> persons;
/** unnecessary things **/
xhtml:
<p:selectOneMenu >
<f:selectItems value="#{personBean.persons}"/>
</p:selectOneMenu>
转换器:
@ManagedBean
@RequestScoped
public class PersonConverter implements Converter{
@ManagedProperty(value = "personBean")
private PersonBean personBean;
@Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
//null & empty checks
Long id = Long.valueOf(value);
for(Person person : personBean.getPersons()){
if(person.getId().equals(id)){
return person;
}
}
throw new ConverterException("some text");
}
@Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
//null & Instanceof checks
return String.valueOf(((Person)value).getId());
}
}
总结:这个解决方案效果很好。但我发现,作为每个组件的转换器,必须有更好的解决方案。
第二个解决方案:我在stackoverflow上找到了全局实体转换器。一个转换器,我认为这是一个很好的解决方案。(“p:自动完成全局实体转换器”)。我使用它,我认为它工作正常。但是经过几次测试,我发现了另一个大问题,实体的版本。
实体转换器的问题1:
我的hashcode或equals中没有版本字段(我没有发现任何关于它的内容)。我只读了这个(JPA hashCode()/equals()困境)。问题是实体不会在hashmap中替换,在某些情况下,我得到一个乐观的锁定异常,因为“旧”实体留在hashmap中。
if (!entities.containsKey(entity)) {
String uuid = UUID.randomUUID().toString();
entities.put(entity, uuid);
return uuid;
} else {
return entities.get(entity);
}
解决方案:我认为我可以通过向实体添加一个接口来解决这个问题,该接口可以检查版本是否存在。
接口:
public interface EntityVersionCheck {
public boolean hasVersion();
}
实施:
@Override
public boolean hasVersion() {
return true;
}
转换器:
@Override
public String getAsString(FacesContext context, UIComponent component, Object entity) {
synchronized (entities) {
if(entity instanceof EntityVersionCheck && ((EntityVersionCheck)entity).hasVersion()){
entities.remove(entity);
}
if (!entities.containsKey(entity)) {
String uuid = UUID.randomUUID().toString();
entities.put(entity, uuid);
return uuid;
} else {
return entities.get(entity);
}
}
}
此解决方案适用于乐观锁定异常,但带来了另一个问题!
实体转换器的问题2:
<p:selectOneMenu value="#{organisation.leader}">
<f:selectItems value="#{personBean.persons}"/>
</p:selectOneMenu>
如果一个组织已经有了一个领导者。如果领导者也在人员列表中,它将被替换为一个新的uuid。领导者将被设置为null或转换异常,因为uuid在hashmap中不再存在。这意味着他将转换器用于organisation.leader并将领导者添加到hashmap中。接下来是人员-列出并添加hashmap中的所有其他人,如果他也存在于人员中,则覆盖organisation.leader中的uuid。
现在有两个案例:
>
当我选择其他领导者时,它正常工作。
如果我不更改“当前”选择并提交组织。领导者试图找到他的“旧”uuid,但人员列表中的另一个人已经覆盖了该uuid,并且uuid不存在,组织也不存在。leader为空。
我找到了另一个解决方案,这是我的最终解决方案,但我发现,这是一个非常非常奇怪的解决方案,我会做得更好,但我什么也没发现。
最终解决方案我将“旧”uuid添加到“新”对象中。
@Override
public String getAsString(FacesContext context, UIComponent component,
Object entity) {
synchronized (entities) {
String currentuuid = null;
if (entity instanceof EntityVersionCheck
&& ((EntityVersionCheck) entity).hasVersion()) {
currentuuid = entities.get(entity);
entities.remove(entity);
}
if (!entities.containsKey(entity)) {
if (currentuuid == null) {
currentuuid = UUID.randomUUID().toString();
}
entities.put(entity, currentuuid);
return currentuuid;
} else {
return entities.get(entity);
}
}
}
问题:我如何让这一切变得更好和正确?
如果解决方案1奏效,而您只是希望它更通用:
将您的实例保持在bean的范围内,您可以通过从中删除托管bean查找来使用更通用的转换器。您的实体应该从具有标识符属性的基本实体继承。您可以在检索实体的bean中实例化此转换器。
或者如果id不应该在html源代码中公开,请使用guid map或公共标识符。
@ManagedBean(name = "personBean")
@ViewScoped
public class PersonBean implements Serializable {
private List<Person> persons;
private EntityConverter<Person> converter;
// this.converter = new EntityConverter<>(persons);
}
<p:selectOneMenu converter="#{personBean.converter}">
<f:selectItems value="#{personBean.persons}"/>
</p:selectOneMenu>
抽象基本实体转换器:
/**
* Abstract Entity Object JSF Converter which by default converts by {@link Entity#getId()}
*/
public abstract class AEntityConverter<T extends Entity> implements Converter
{
@Override
public String getAsString(final FacesContext context, final UIComponent component, final Object value)
{
if (value instanceof Entity)
{
final Entity entity = (Entity) value;
if (entity.getId() != null)
return String.valueOf(entity.getId());
}
return null;
}
}
缓存的集合:
/**
* Entity JSF Converter which holds a Collection of Entities
*/
public class EntityConverter<T extends Entity> extends AEntityConverter<T>
{
/**
* Collection of Entity Objects
*/
protected Collection<T> entities;
/**
* Creates a new Entity Converter with the given Entity Object's
*
* @param entities Collection of Entity's
*/
public EntityConverter(final Collection<T> entities)
{
this.entities = entities;
}
@Override
public Object getAsObject(final FacesContext context, final UIComponent component, final String value)
{
if (value == null || value.trim().equals(""))
return null;
try
{
final int id = Integer.parseInt(value);
for (final Entity entity : this.entities)
if (entity.getId().intValue() == id)
return entity;
}
catch (final RuntimeException e)
{
// do something --> redirect to exception site
}
return null;
}
@Override
public void setEntities(final Collection<T> entities)
{
this.entities = entities;
}
@Override
public Collection<T> getEntities()
{
return this.entities;
}
}
远程或数据库查找转换器:
/**
* Entity Object JSF Converter
*/
public class EntityRemoteConverter<T extends Entity> extends AEntityConverter<T>
{
/**
* Dao
*/
protected final Dao<T> dao;
public EntityRemoteConverter(final EntityDao<T> dao)
{
this.dao = dao;
}
@Override
public Object getAsObject(final FacesContext context, final UIComponent component, final String value)
{
// check for changed value
// no need to hit database or remote server if value did not changed!
if (value == null || value.trim().equals(""))
return null;
try
{
final int id = Integer.parseInt(value);
return this.dao.getEntity(id);
}
catch (final RuntimeException e)
{
// do someting
}
return null;
}
}
每当我需要转换视图参数时,我都会使用dao方法,而bean还没有构建。
避免昂贵的查找在道方法中,您应该在进行潜在的昂贵查找之前检查值是否已更改,因为转换器可以在不同阶段内多次调用。
看看来源:http://showcase.omnifaces.org/converters/ValueChangeConverter
这种基本方法非常灵活,可以很容易地扩展到许多用例中。
然后开始正确地调用转换器。这向我提出了一个问题,即何时应该命名转换器(通过属性),以及何时告诉他们转换器应该与哪个类一起使用(与属性一起使用)就足够了。在使用PrimeFaces时,我从来不需要命名任何转换器,只是针对这个特定的组件。不同的元件对转换器有不同的需求吗?还是我只是把转换器的概念搞错了?
在我的代码中,我想要 对象“膳食创建”中字段“日期时间”上的字段错误:拒绝值[2020-05-11T11:08];代码[TypeMismatch.MealScreate.DateTime,TypeMismatch.DateTime,TypeMismatch.java.time.LocalDateTime,TypeMismatch];参数[org.springframework.context.su
问题内容: 我正在编写我的第一个Java EE 6 Web应用程序作为学习练习。我没有使用框架,只有JPA 2.0,EJB 3.1和JSF 2.0。 我有一个自定义转换器,可以将存储在SelectOne组件中的JPA实体转换回实体。我正在使用InitialContext.lookup获取对会话Bean的引用以查找相关实体。 我想创建一个通用的实体转换器,所以不必为每个实体创建一个转换器。我以为我会
主要内容:转换器标签JSF有转换器将其UI组件的数据转换为托管bean中使用的对象,反之亦然。 例如,我们可以将文本转换为日期对象,并且可以验证输入的格式。 要使用转换器标签,我们必须在节点中使用URI的以下命名空间。 转换器标签 以下是JSF 2.0中的重要转换代码: 标签 描述 将字符串转换为所需格式的数字值 将字符串转换为所需的日期格式 自定义转换器 创建一个自定义转换器
我正在使用JmsTemplate转换和发送事件对象。我已经向JmsTemplate注册了MappingJackson2MessageConverter。 我正在使用并从该主题获取消息。我不知道如何将此消息转换回我的对象? JmsTemplate的messageconverter是 我尝试调用并传递此消息对象进行转换,但没有成功。如何将我收到的消息对象转换为我想要的自定义对象?
主要内容:JSF自定义转换器实例我们可以在JSF中创建自己的自定义转换器。 以下列表是我们可以在JSF中创建自定义转换器的步骤。 通过实现接口创建一个转换器类。 实现上述接口的和方法。 使用注解为自定义转换器分配唯一的ID。 JSF自定义转换器实例 打开 NetBeans IDE 创建一个Web工程:CustomConverter,其目录结构如下所示 - 创建以下文件代码,文件:index.xhtml 的代码内容如下所示 - 文