Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/jsf/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用作JSF值的Java记录_Jsf_El_Java Record - Fatal编程技术网

用作JSF值的Java记录

用作JSF值的Java记录,jsf,el,java-record,Jsf,El,Java Record,我正在开发一个简单的JSF web应用程序,主要是为了学习使用Java 15和JSF 2.3以及PrimeFaces 8,我正在使用一个简单的Java记录来建模实体。应用程序不使用任何数据库 我的问题是,是否可以像这样在xhtml页面中使用java记录作为值 <p:column headerText="Id"> <h:outputText value="#{car.randomId}" /> <

我正在开发一个简单的JSF web应用程序,主要是为了学习使用Java 15和JSF 2.3以及PrimeFaces 8,我正在使用一个简单的Java记录来建模实体。应用程序不使用任何数据库

我的问题是,是否可以像这样在xhtml页面中使用java记录作为值

    <p:column headerText="Id">
        <h:outputText value="#{car.randomId}" />
    </p:column>

在pom.xml中,我使用以下api

        <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-api</artifactId>
            <version>8.0.0</version>
            <scope>provided</scope>
        </dependency>

谢谢你的帮助

在深入挖掘堆栈跟踪并检查用于读取属性的代码之后,似乎目前还没有实现从java记录中读取属性

也许在未来的版本中

请注意,我将在找到有关此主题的相关信息后尽快更新答案

更新8.11.2020 不幸的是,我误解了记录的范围


在现实世界的应用程序中,我的实体将是一个JPA实体。

在深入挖掘堆栈跟踪并检查用于读取属性的代码后,似乎目前还没有实现从java记录中读取属性

也许在未来的版本中

请注意,我将在找到有关此主题的相关信息后尽快更新答案

更新8.11.2020 不幸的是,我误解了记录的范围


在真实的应用程序中,我的实体将是一个JPA实体。

从技术上讲,问题不在JSF中,而在EL中。异常的包名已经提示:javax.el.PropertyNotFoundException。正在使用的EL版本还不能识别Java记录。Jakarta EE 8与Java8绑定,但Java14中引入了Java记录功能。理论上,它最早只能在与Java14相关联的Jakarta EE版本相关联的EL版本中本机支持。但即使这样也不太可能,因为Java记录仅作为预览功能可用,因此默认情况下根本不启用

回到您的具体问题,使用{car.randomId}中的方法表达式语法在WildFly 21上确实对我有效。在任何情况下,都可以使用自定义ELResolver自定义EL解析。下面是一个启动示例,它基于ClassSrecord检查记录,并通过ClassgetRecordComponents收集可用字段作为属性:

public class RecordELResolver extends ELResolver {

    private static final Map<Class<?>, Map<String, PropertyDescriptor>> RECORD_PROPERTY_DESCRIPTOR_CACHE = new ConcurrentHashMap<>();

    private static boolean isRecord(Object base) {
        return base != null && base.getClass().isRecord();
    }
    
    private static Map<String, PropertyDescriptor> getRecordPropertyDescriptors(Object base) {
        return RECORD_PROPERTY_DESCRIPTOR_CACHE
            .computeIfAbsent(base.getClass(), clazz -> Arrays
                .stream(clazz.getRecordComponents())
                .collect(Collectors
                    .toMap(RecordComponent::getName, recordComponent -> {
                        try {
                            return new PropertyDescriptor(recordComponent.getName(), recordComponent.getAccessor(), null);
                        }
                        catch (IntrospectionException e) {
                            throw new IllegalStateException(e);
                        }
                    })));
    }
    
    private static PropertyDescriptor getRecordPropertyDescriptor(Object base, Object property) {
        PropertyDescriptor descriptor = getRecordPropertyDescriptors(base).get(property);
        
        if (descriptor == null) {
            throw new PropertyNotFoundException("The record '" + base.getClass().getName() + "' does not have the field '" + property + "'.");
        }

        return descriptor;
    }

    @Override
    public Object getValue(ELContext context, Object base, Object property) {
        if (!isRecord(base) || property == null) {
            return null;
        }

        PropertyDescriptor descriptor = getRecordPropertyDescriptor(base, property);

        try {
            Object value = descriptor.getReadMethod().invoke(base);
            context.setPropertyResolved(base, property);
            return value;
        }
        catch (Exception e) {
            throw new ELException(e);
        }
    }

    @Override
    public Class<?> getType(ELContext context, Object base, Object property) {
        if (!isRecord(base) || property == null) {
            return null;
        }

        PropertyDescriptor descriptor = getRecordPropertyDescriptor(base, property);
        context.setPropertyResolved(true);
        return descriptor.getPropertyType();
    }

    @Override
    public Class<?> getCommonPropertyType(ELContext context, Object base) {
        if (!isRecord(base)) {
            return null;
        }

        return String.class;
    }

    @Override
    public boolean isReadOnly(ELContext context, Object base, Object property) {
        if (!isRecord(base)) {
            return false;
        }

        getRecordPropertyDescriptor(base, property); // Forces PropertyNotFoundException if necessary.
        context.setPropertyResolved(true);
        return true;
    }

    @Override
    public void setValue(ELContext context, Object base, Object property, Object value) {
        if (!isRecord(base)) {
            return;
        }

        throw new PropertyNotWritableException("Java Records are immutable");
    }

    @Override
    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
        if (!isRecord(base)) {
            return null;
        }

        Map rawDescriptors = getRecordPropertyDescriptors(base);
        return rawDescriptors.values().iterator();
    }

}
实际工作是用getValue方法完成的。它基本上定位表示java记录访问器的java.lang.reflect.Method并调用它


这就是说,Java记录不适合替代完整的JavaBean,主要是因为Java记录是不可变的。因此,它们不能用作真正的JPA实体,因为它们应该是可变的。Java记录作为DTO最有用。

从技术上讲,问题不在于JSF,而在于EL。异常的包名已经提示:javax.el.PropertyNotFoundException。正在使用的EL版本还不能识别Java记录。Jakarta EE 8与Java8绑定,但Java14中引入了Java记录功能。理论上,它最早只能在与Java14相关联的Jakarta EE版本相关联的EL版本中本机支持。但即使这样也不太可能,因为Java记录仅作为预览功能可用,因此默认情况下根本不启用

回到您的具体问题,使用{car.randomId}中的方法表达式语法在WildFly 21上确实对我有效。在任何情况下,都可以使用自定义ELResolver自定义EL解析。下面是一个启动示例,它基于ClassSrecord检查记录,并通过ClassgetRecordComponents收集可用字段作为属性:

public class RecordELResolver extends ELResolver {

    private static final Map<Class<?>, Map<String, PropertyDescriptor>> RECORD_PROPERTY_DESCRIPTOR_CACHE = new ConcurrentHashMap<>();

    private static boolean isRecord(Object base) {
        return base != null && base.getClass().isRecord();
    }
    
    private static Map<String, PropertyDescriptor> getRecordPropertyDescriptors(Object base) {
        return RECORD_PROPERTY_DESCRIPTOR_CACHE
            .computeIfAbsent(base.getClass(), clazz -> Arrays
                .stream(clazz.getRecordComponents())
                .collect(Collectors
                    .toMap(RecordComponent::getName, recordComponent -> {
                        try {
                            return new PropertyDescriptor(recordComponent.getName(), recordComponent.getAccessor(), null);
                        }
                        catch (IntrospectionException e) {
                            throw new IllegalStateException(e);
                        }
                    })));
    }
    
    private static PropertyDescriptor getRecordPropertyDescriptor(Object base, Object property) {
        PropertyDescriptor descriptor = getRecordPropertyDescriptors(base).get(property);
        
        if (descriptor == null) {
            throw new PropertyNotFoundException("The record '" + base.getClass().getName() + "' does not have the field '" + property + "'.");
        }

        return descriptor;
    }

    @Override
    public Object getValue(ELContext context, Object base, Object property) {
        if (!isRecord(base) || property == null) {
            return null;
        }

        PropertyDescriptor descriptor = getRecordPropertyDescriptor(base, property);

        try {
            Object value = descriptor.getReadMethod().invoke(base);
            context.setPropertyResolved(base, property);
            return value;
        }
        catch (Exception e) {
            throw new ELException(e);
        }
    }

    @Override
    public Class<?> getType(ELContext context, Object base, Object property) {
        if (!isRecord(base) || property == null) {
            return null;
        }

        PropertyDescriptor descriptor = getRecordPropertyDescriptor(base, property);
        context.setPropertyResolved(true);
        return descriptor.getPropertyType();
    }

    @Override
    public Class<?> getCommonPropertyType(ELContext context, Object base) {
        if (!isRecord(base)) {
            return null;
        }

        return String.class;
    }

    @Override
    public boolean isReadOnly(ELContext context, Object base, Object property) {
        if (!isRecord(base)) {
            return false;
        }

        getRecordPropertyDescriptor(base, property); // Forces PropertyNotFoundException if necessary.
        context.setPropertyResolved(true);
        return true;
    }

    @Override
    public void setValue(ELContext context, Object base, Object property, Object value) {
        if (!isRecord(base)) {
            return;
        }

        throw new PropertyNotWritableException("Java Records are immutable");
    }

    @Override
    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
        if (!isRecord(base)) {
            return null;
        }

        Map rawDescriptors = getRecordPropertyDescriptors(base);
        return rawDescriptors.values().iterator();
    }

}
实际工作是用getValue方法完成的。它基本上定位表示java记录访问器的java.lang.reflect.Method并调用它


这就是说,Java记录不适合替代完整的JavaBean,主要是因为Java记录是不可变的。因此,它们不能用作真正的JPA实体,因为它们应该是可变的。Java记录作为DTO最有用。

请您分享car类,以便更好地帮助您吗?事实上,car类,该记录是否构成问题,但这里是公共记录car字符串randomId,字符串randomBrand,int randomYear,字符串randomColor,int randomPrice,布尔randomSoldState{}请分享car类,为了更好地帮助您?实际上是汽车类,这是记录形式的问题,但这里是公共记录汽车字符串randomId,字符串randomBrand,int randomYear,字符串randomColor,int randomPrice,boolean randomSoldState{}非常感谢您的详细解释和代码示例!小细节:getCommonPropertyType的return String.class-当您使用字符串寻址记录组件时。GetFeatureDescriptor的return String.class:return record_COMPONENT_CACHE.computeIfAbsent….values.mapp->new java.beans.propertyDescriptor.getNam
e、 p.getAccessor,null.iterator;“可能有用。”@Johannes:完全正确。这只是一个基于现有javax.el.BeanELResolver的启动示例。提到的方法实际上从未在正常的JSF生命周期中调用过,但我会更新代码。非常感谢您的附加注释。非常感谢您的详细解释和代码示例!小细节:getCommonPropertyType的return String.class-当您使用字符串寻址记录组件时。getFeatureDescriptor的return String.class:return record_COMPONENT_CACHE.ComputeFabSent….values.mapp->new java.beans.propertyDescriptor.getName,p.getAccessor,null.iterator;“可能有用。”@Johannes:完全正确。这只是一个基于现有javax.el.BeanELResolver的启动示例。在正常的JSF生命周期中,上述方法实际上从未被调用过,但我将更新代码。非常感谢附加的注释。
<application>
    <el-resolver>com.example.RecordELResolver</el-resolver>
</application>