Создайте один JSF-конвертер для нескольких типов [duplicate]

Самый простой ответ на этот вопрос - добавить.

css

float: left;

codepen link: http://jsfiddle.net/dGHFV/3560/

6
задан BalusC 27 June 2013 в 13:56
поделиться

2 ответа

Самый простой способ - позволить всем вашим сущностям JPA проходить от базового объекта следующим образом:

public abstract class BaseEntity<T extends Number> implements Serializable {

    private static final long serialVersionUID = 1L;

    public abstract T getId();

    public abstract void setId(T id);

    @Override
    public int hashCode() {
        return (getId() != null) 
            ? (getClass().getSimpleName().hashCode() + getId().hashCode())
            : super.hashCode();
    }

    @Override
    public boolean equals(Object other) {
        return (other != null && getId() != null
                && other.getClass().isAssignableFrom(getClass()) 
                && getClass().isAssignableFrom(other.getClass())) 
            ? getId().equals(((BaseEntity<?>) other).getId())
            : (other == this);
    }

    @Override
    public String toString() {
        return String.format("%s[id=%d]", getClass().getSimpleName(), getId());
    }

}

Обратите внимание, что важно иметь правильные equals()hashCode()), иначе вы столкнется с Ошибка проверки: значение недействительно . Тесты Class#isAssignableFrom() состоят в том, чтобы избежать неудачных тестов, например. Прокси-серверы на спящем режиме без необходимости возвращаться к вспомогательному методу Hibernate#getClass(Object) с гибернатом.

И иметь базовую услугу, подобную этой (да, я игнорирую тот факт, что вы используете Spring, это просто дать базовую идею):

@Stateless
public class BaseService {

    @PersistenceContext
    private EntityManager em;

    public BaseEntity<? extends Number> find(Class<BaseEntity<? extends Number>> type, Number id) {
        return em.find(type, id);
    }

}

И реализовать преобразователь следующим образом:

@ManagedBean
@ApplicationScoped
@SuppressWarnings({ "rawtypes", "unchecked" }) // We don't care about BaseEntity's actual type here.
public class BaseEntityConverter implements Converter {

    @EJB
    private BaseService baseService;

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        if (value == null) {
            return "";
        }

        if (modelValue instanceof BaseEntity) {
            Number id = ((BaseEntity) modelValue).getId();
            return (id != null) ? id.toString() : null;
        } else {
            throw new ConverterException(new FacesMessage(String.format("%s is not a valid User", modelValue)), e);
        }
    }

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        if (value == null || value.isEmpty()) {
            return null;
        }

        try {
            Class<?> type = component.getValueExpression("value").getType(context.getELContext());
            return baseService.find((Class<BaseEntity<? extends Number>>) type, Long.valueOf(submittedValue));
        } catch (NumberFormatException e) {
            throw new ConverterException(new FacesMessage(String.format("%s is not a valid ID of BaseEntity", submittedValue)), e);
        }
    }

}

Обратите внимание, что он зарегистрирован как @ManagedBean вместо @FacesConverter. Этот трюк позволяет вам вводить услугу в преобразователь, например, @EJB. См. Также . Как вставлять @EJB, @PersistenceContext, @Inject, @Autowired и т. Д. В @FacesConverter? Поэтому вам нужно ссылаться на него как converter="#{baseEntityConverter}" вместо converter="baseEntityConverter".

Если вы используете такой преобразователь чаще, чем для компонентов UISelectOne / UISelectMany (<h:selectOneMenu> и друзей), вы можете найти OmniFaces SelectItemsConverter гораздо полезнее. Он преобразуется на основе значений, доступных в <f:selectItems>, вместо того, чтобы делать (потенциально дорогостоящие) вызовы БД каждый раз.

16
ответ дан Community 24 August 2018 в 17:37
поделиться

Вот мое решение с этими соображениями:

  • Я предполагаю, что вас интересует JPA (не Hibernate)
  • Мое решение не требует расширения любого класса и должно работать для любого компонента сущности JPA, это всего лишь простой класс, который вы используете, а также не требует выполнения каких-либо услуг или DAO. Единственное требование заключается в том, что конвертер напрямую зависит от библиотеки JPA, которая может быть не очень элегантной.
  • Он использует вспомогательные методы для сериализации / десериализации идентификатора компонента. Он преобразует только идентификатор сущности объекта и связывает строку с именем класса и идентификатором, сериализованным и преобразованным в base64. Это возможно из-за того, что в jpa идентификаторы объектов должны реализовать сериализуемое. Реализация этих методов в java 1.7, но вы можете найти другие реализации для java & lt; 1.7 там
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.persistence.EntityManagerFactory;

/**
 * Generic converter of jpa entities for jsf
 * 
 * Converts the jpa instances to strings with this form: @ Converts from strings to instances searching by id in
 * database
 * 
 * It is possible thanks to the fact that jpa requires all entity ids to
 * implement serializable
 * 
 * Requires: - You must provide instance with name "entityManagerFactory" to be
 * injected - Remember to implement equals and hashCode in all your entity
 * classes !!
 * 
 */
@ManagedBean
@RequestScoped
public class EntityConverter implements Converter {

    private static final char CHARACTER_SEPARATOR = '@';

    @ManagedProperty(value = "#{entityManagerFactory}")
    private EntityManagerFactory entityManagerFactory;

    public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }

    private static final String empty = "";

    @Override
    public Object getAsObject(FacesContext context, UIComponent c, String value) {
        if (value == null || value.isEmpty()) {
            return null;
        }

        int index = value.indexOf(CHARACTER_SEPARATOR);
        String clazz = value.substring(0, index);
        String idBase64String = value.substring(index + 1, value.length());
EntityManager entityManager=null;
        try {
            Class entityClazz = Class.forName(clazz);
            Object id = convertFromBase64String(idBase64String);

        entityManager = entityManagerFactory.createEntityManager();
        Object object = entityManager.find(entityClazz, id);

            return object;

        } catch (ClassNotFoundException e) {
            throw new ConverterException("Jpa entity not found " + clazz, e);
        } catch (IOException e) {
            throw new ConverterException("Could not deserialize id of jpa class " + clazz, e);
        }finally{
        if(entityManager!=null){
            entityManager.close();  
        }
    }

    }

    @Override
    public String getAsString(FacesContext context, UIComponent c, Object value) {
        if (value == null) {
            return empty;
        }
        String clazz = value.getClass().getName();
        String idBase64String;
        try {
            idBase64String = convertToBase64String(entityManagerFactory.getPersistenceUnitUtil().getIdentifier(value));
        } catch (IOException e) {
            throw new ConverterException("Could not serialize id for the class " + clazz, e);
        }

        return clazz + CHARACTER_SEPARATOR + idBase64String;
    }

    // UTILITY METHODS, (Could be refactored moving it to another place)

    public static String convertToBase64String(Object o) throws IOException {
        return javax.xml.bind.DatatypeConverter.printBase64Binary(convertToBytes(o));
    }

    public static Object convertFromBase64String(String str) throws IOException, ClassNotFoundException {
        return convertFromBytes(javax.xml.bind.DatatypeConverter.parseBase64Binary(str));
    }

    public static byte[] convertToBytes(Object object) throws IOException {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutput out = new ObjectOutputStream(bos)) {
            out.writeObject(object);
            return bos.toByteArray();
        }
    }

    public static Object convertFromBytes(byte[] bytes) throws IOException, ClassNotFoundException {
        try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInput in = new ObjectInputStream(bis)) {
            return in.readObject();
        }
    }

}

Используйте его как другой преобразователь с

<h:selectOneMenu converter="#{entityConverter}" ...
0
ответ дан Karl Richter 24 August 2018 в 17:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: