Самый простой ответ на этот вопрос - добавить.
css
float: left;
codepen link: http://jsfiddle.net/dGHFV/3560/
Самый простой способ - позволить всем вашим сущностям 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>
, вместо того, чтобы делать (потенциально дорогостоящие) вызовы БД каждый раз.
Вот мое решение с этими соображениями:
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}" ...