Final solution of the problem for getting the converter, equals and JSF code to work.
The Player is a Player bean with a few properties. The service access the Player and writes player info to the database (besides the point :)
<h:selectOneListbox size="1" value="#{player}"
converter="playerConverter" id="playerList">
<f:selectItems value="#{servicePlayer.allPlayers}"
var="n"
itemValue="#{n}"
itemLabel="#{n.combinedName}"
itemLabelEscaped="true"/>
</h:selectOneListbox>
In ServicePlayer
public List<Player> getAllPlayers() {
if (factory == null) {
factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
}
EntityManager em = factory.createEntityManager();
Query q = em.createQuery("select t from Player t");
List<Player> players = q.getResultList();
....
#FacesConverter(value = "playerConverter")
public class PlayerConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null) {
return null;
}
long idValue;
try {
idValue = Long.parseLong(value);
}
catch (NumberFormatException ex)
{
return null;
}
ServicePlayer servicePlayer = context.getApplication()
.evaluateExpressionGet(context, "#{servicePlayer}",
ServicePlayer.class);
Player player = servicePlayer.getPlayerByID(idValue);
return player;
}
#Override
public String getAsString(FacesContext context,
UIComponent component, Object value) {
if (value == null || value.equals("")) {
return "";
} else {
return String.valueOf(((Player)value).getStringID());
}
}
}
In Player
#Override
public boolean equals(Object other) {
return (id != null && other != null && getClass() == other.getClass())
? id.equals(((Player) other).getId())
: (other == this);
}
#Override
public int hashCode() {
return (id != null)
? (getClass().hashCode() + id.hashCode())
: super.hashCode();
}
in getAsString you are returning a String object, and in getAsObject you are trying to parse the value as Long: idValue = Long.parseLong(value); which does not make any sense.
You should return the proper id in getAsString so you can get it in getAsObject.
Updated
E.g.
Your getAsString method should look like :
public String getAsString(FacesContext facesContext, UIComponent component, Object value) {
if (value == null || value.equals("")) {
return "";
}
else {
return String.valueOf(((ServicePlayer) value).getId());
}
}
It should be like this.
<f:selectItems value="#{servicePlayer.allPlayers}"
var="n"
itemValue="#{n}"
itemLabel="#{n.combinedName}"
itemLabelEscaped="true"/>
</h:selectOneListbox>
The itemValue should be the player object (n), NOT n.stringID, since the value of the h:selectOneListbox is a player object.
getAsObject
#Override
public Object getAsObject(FacesContext context,
UIComponent component, String value) {
if (value == null) {
return null;
}
long idValue;
try {
idValue = Long.parseLong(value);
}
catch (NumberFormatException ex)
{
return null;
}
ServicePlayer servicePlayer = context.getApplication()
.evaluateExpressionGet(context, "#{servicePlayer}",
ServicePlayer.class);
Player player = servicePlayer.getPlayerByID(idValue);
return player;
}
getAsString
#Override
public String getAsString(FacesContext context,
UIComponent component, Object value) {
if (value == null || value.equals("")) {
return "";
} else {
return String.valueOf(((Player)value).getStringID());
//or what ever the id is
}
}
EDIT:
Please note that the Player MUST implement the equals and the hashcode methods.
See Also:
Validation error value is not valid.
Hope this helps.
Related
I have to objects Client and Order and these objects are living in bidirectional relation and I try to write them to file, but I get StackOverflowError. I got this error because my equals methods are looped.
My classes which I try to serialize:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Client implements Serializable {
private Long id;
private String name;
private List<Order> orders = new ArrayList<>();
public void addOrder(Order order) {
order.setClient(this);
orders.add(order);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Client client = (Client) o;
if (id != null ? !id.equals(client.id) : client.id != null) return false;
if (name != null ? !name.equals(client.name) : client.name != null) return false;
return orders != null ? orders.equals(client.orders) : client.orders == null;
}
#Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (orders != null ? orders.hashCode() : 0);
return result;
}
#Override
public String toString() {
return "Client{" +
"id=" + id +
", name='" + name + '\'' +
// ", orders=" + orders.size() +
'}';
}
}
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Order implements Serializable {
private Long id;
private String name;
private Client client;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Order order = (Order) o;
if (id != null ? !id.equals(order.id) : order.id != null) return false;
if (name != null ? !name.equals(order.name) : order.name != null) return false;
return client != null ? client.equals(order.client) : order.client == null;
}
#Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (client != null ? client.hashCode() : 0);
return result;
}
#Override
public String toString() {
return "Order{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
#Data
#AllArgsConstructor
public class MapDataSource implements Serializable {
private final Map<Date, List<Client>> clients = new HashMap<>();
private final Map<Date, List<Order>> orders = new HashMap<>();
}
#Slf4j
public class ObjectWriter {
private final String fileName = "data.obj";
public void write(String fileName, MapDataSource mapDataSource) {
try (
FileOutputStream fs = new FileOutputStream(fileName);
ObjectOutputStream oos = new ObjectOutputStream(fs)
) {
oos.writeObject(mapDataSource);
log.info("Object has been written.");
} catch (IOException ioe) {}
}
}
#Slf4j
public class ObjectReader {
private static final String fileName = "data.obj";
public MapDataSource readObj(String fileName) {
MapDataSource mapDataSource = null;
try (
FileInputStream fis = new FileInputStream(fileName);
ObjectInputStream ois = new ObjectInputStream(fis)
) {
mapDataSource = ((MapDataSource) ois.readObject());
// log.info("Read object: {}", mapDataSource);
} catch (IOException ioe) {
} catch (ClassNotFoundException classEx) {
System.out.println();
}
return mapDataSource;
}
}
And when I try to run code below I get StackOVerflowError:
String testFile = "testFile.obj";
final DateTime time = new DateTime(2017, 12, 1, 10, 0);
final Client client1 = new Client(1L, "Client1", new ArrayList<>());
final Order order1 = new Order(1L, "Order1", null);
final MapDataSource mapDataSource = new MapDataSource();
mapDataSource.getClients().put(time.toDate(), new ArrayList<>());
mapDataSource.getClients().get(time.toDate()).add(client1);
mapDataSource.getOrders().put(time.toDate(), new ArrayList<>());
mapDataSource.getOrders().get(time.toDate()).add(order1);
new ObjectWriter().write(testFile, mapDataSource);
final MapDataSource found = new ObjectReader().readObj(testFile);
System.out.println(found);
Solution:
MapDataSource needs to have implemented equals() and hashcode() methods.
It seems like you need to sit down and seriously consider what it should even mean for two clients or orders to be equal in the first place. The Long id; makes me wonder whether you should really be comparing the object graphs in the first place. If e.g. clients have unique IDs, then it could make sense to just ensure that clients are unique object instances and then you do away with the problem entirely.
If you really do need to compare object graphs, you could use something like the following. We use an IdentityHashMap to record all of the objects we've seen, then if we detect a cycle, we just compare the previously-stored counter values which tells us if the two graphs have the same cycle.
Client and Order need to share code (so the maps can be passed around), so you just override equals in both to return ClientOrderEquality.equals(this, that).
import java.util.*;
public final class ClientOrderEquality {
private ClientOrderEquality() {}
private static final class Counter { long value; }
public static boolean equals(Client lhs, Client rhs) {
return equals(lhs, new IdentityHashMap<>(),
rhs, new IdentityHashMap<>(),
new Counter());
}
public static boolean equals(Order lhs, Order rhs) {
return equals(lhs, new IdentityHashMap<>(),
rhs, new IdentityHashMap<>(),
new Counter());
}
private static boolean equals(Client lhs,
Map<Object, Long> seenL,
Client rhs,
Map<Object, Long> seenR,
Counter counter) {
if (lhs == null || rhs == null)
return lhs == rhs;
Long countL = seenL.putIfAbsent(lhs, counter.value);
Long countR = seenR.putIfAbsent(rhs, counter.value);
if (countL != null || countR != null)
return Objects.equals(countL, countR);
counter.value++;
if (lhs == rhs)
return true;
if (!Objects.equals(lhs.id, rhs.id))
return false;
if (!Objects.equals(lhs.name, rhs.name))
return false;
if (lhs.orders.size() != rhs.orders.size())
return false;
Iterator<Order> itL = lhs.orders.iterator();
Iterator<Order> itR = rhs.orders.iterator();
while (itL.hasNext() && itR.hasNext())
if (!equals(itL.next(), seenL, itR.next(), seenR, counter))
return false;
return true;
}
private static boolean equals(Order lhs,
Map<Object, Long> seenL,
Order rhs,
Map<Object, Long> seenR,
Counter counter) {
if (lhs == null || rhs == null)
return lhs == rhs;
Long countL = seenL.putIfAbsent(lhs, counter.value);
Long countR = seenR.putIfAbsent(rhs, counter.value);
if (countL != null || countR != null)
return Objects.equals(countL, countR);
counter.value++;
if (lhs == rhs)
return true;
if (!Objects.equals(lhs.id, rhs.id))
return false;
if (!Objects.equals(lhs.name, rhs.name))
return false;
return equals(lhs.client, seenL, rhs.client, seenR, counter);
}
}
I assume if you want to actually use that code, you'll need to alter it to use whatever getter naming format you're using and write a hashCode implementation. You'll also need to consider subtypes correctly if you're extending Client and Order.
I'm trying to refactoring this code
private void validate(Customer customer) {
List<String> errors = new ArrayList<>();
if (customer == null) {
errors.add("Customer must not be null");
}
if (customer != null && customer.getName() == null) {
errors.add("Name must not be null");
}
if (customer != null && customer.getName().isEmpty()) {
errors.add("Name must not be empty");
}
if (customer != null) {
Customer customerFromDb = customerRepository.findByName(customer.getName());
if (customerFromDb != null) {
errors.add("Customer already present on db");
}
}
if (!errors.isEmpty()) {
throw new ValidationException(errors);
}
}
I read this post Business logic validation patterns & advices
I'd like to build a generic validator for my entities and fields of the entity, I wrote this
private void validate(Customer customer) {
List<ValidationRule> validationRules = new ArrayList<>();
validationRules.add(new NotNullValidationRule(customer));
validationRules.add(new NotNullValidationRule(customer, Customer::getName));
validationRules.add(new NotEmptyValidationRule(customer, Customer::getName));
validationRules.add(new NotExistValidationRule(customer -> customerRepository.findByName(customer.getName())));
Validator.validate(validationRules);
}
and the Validator class
public class Validator {
public static void validate(List<ValidationRule> validationRules) {
final List<String> errors = new ArrayList<>();
for (final ValidationRule rule : validationRules) {
final Optional<String> error = rule.validate();
if (error.isPresent()) {
errors.add(error.get());
}
}
if (!errors.isEmpty()) {
throw new ValidationException(errors);
}
}
}
but I don't know how to implement the interface ValidationRule and other classes (NotNullValidationRule, NotEmptyValidationRule, NotExistValidationRule)
I would write something like :
CommonValidations.notNull(errors, customer);
if (customer != null) {
CommonValidations.notEmpty(errors, customer.getName());
}
customerCustomeBeanValidations.validName(errors, customer.getName());
customerCustomeBeanValidations.notExist(errors, customer.getName());
In the link you reference, the accepted answer suggested using the Strategy design pattern, and then gave an example of both an interface and implementation. In your case, you would create a new interface ValidationRule, with at least one method validate(), and then you would create concrete classes that each implementat that interface (NotNullValidationRule, NotEmptyValidationRule, AlreadyExistValidationRule).
I found this solution:
I create an interface ValidationRule
import java.util.Optional;
public interface ValidationRule {
Optional<ValidationError> validate();
}
And some classes that implement the behaviours
public class NotNullValidationRule implements ValidationRule {
private Object object;
private String field;
public NotNullValidationRule(Object object, String field) {
this.object = object;
if (field == null || field.isEmpty()) {
throw new IllegalArgumentException("field must not be null or emtpy");
}
this.field = field;
}
#Override public Optional<ValidationError> validate() {
if (object == null) {
return Optional.empty();
}
try {
Object value = new PropertyDescriptor(field, object.getClass()).getReadMethod().invoke(object);
if (value == null) {
ValidationError validationError = new ValidationError();
validationError.setName(object.getClass().getSimpleName() + "." + field);
validationError.setError("Field " + field + " is null");
return Optional.of(validationError);
}
}
catch (Exception e) {
throw new IllegalStateException("error during retrieve of field value");
}
return Optional.empty();
}
}
Another where I pass a method to call:
package it.winetsolutions.winactitime.core.service.validation;
import java.beans.PropertyDescriptor;
import java.util.Optional;
import java.util.function.Function;
public class NotExistValidationRule implements ValidationRule {
Object object;
String field;
Function<? super String, ? super Object> function;
public NotExistValidationRule(Object object, String field, Function<? super String, ? super Object> function) {
this.object = object;
if (field == null || field.isEmpty() || function == null) {
throw new IllegalArgumentException("field and function must not be null or emtpy");
}
this.field = field;
this.function = function;
}
#Override public Optional<ValidationError> validate() {
if (object == null) {
return Optional.empty();
}
try {
Object value = new PropertyDescriptor(field, object.getClass()).getReadMethod().invoke(object);
Long id = (Long) new PropertyDescriptor("id", object.getClass()).getReadMethod().invoke(object);
Object result = function.apply(value == null ? (String) value : ((String) value).trim());
if (result != null &&
!id.equals((Long) new PropertyDescriptor("id", result.getClass()).getReadMethod().invoke(result))) {
ValidationError validationError = new ValidationError();
validationError.setName(object.getClass().getSimpleName() + "." + field);
validationError.setError("Element with " + field +": " + value + " already exists");
return Optional.of(validationError);
}
}
catch (Exception e) {
throw new IllegalStateException("error during retrieve of field value");
}
return Optional.empty();
}
}
This code:
#Override
public List<FactCodeDto> getAllFactsWithoutParentsAsFactDto() {
String completeQuery = FactCodeQueries.SELECT_DTO_FROM_FACT_WITH_NO_PARENTS;
Query query = createHibernateQueryForUnmappedTypeFactDto(completeQuery);
List<FactCodeDto> factDtoList = query.list(); //line 133
return factDtoList;
}
calling this method:
private Query createHibernateQueryForUnmappedTypeFactDto(String sqlQuery) throws HibernateException {
return FactCodeQueries.addScalars(createSQLQuery(sqlQuery)).setResultTransformer(Transformers.aliasToBean(FactCodeDto.class));
}
gives me a ClassCastException -> part of the trace:
Caused by: java.lang.ClassCastException: org.bamboomy.cjr.dto.FactCodeDto cannot be cast to java.util.Map
at org.hibernate.property.access.internal.PropertyAccessMapImpl$SetterImpl.set(PropertyAccessMapImpl.java:102)
at org.hibernate.transform.AliasToBeanResultTransformer.transformTuple(AliasToBeanResultTransformer.java:78)
at org.hibernate.hql.internal.HolderInstantiator.instantiate(HolderInstantiator.java:75)
at org.hibernate.loader.custom.CustomLoader.getResultList(CustomLoader.java:435)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2423)
at org.hibernate.loader.Loader.list(Loader.java:2418)
at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:336)
at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:1898)
at org.hibernate.internal.AbstractSessionImpl.list(AbstractSessionImpl.java:318)
at org.hibernate.internal.SQLQueryImpl.list(SQLQueryImpl.java:125)
at org.bamboomy.cjr.dao.factcode.FactCodeDAOImpl.getAllFactsWithoutParentsAsFactDto(FactCodeDAOImpl.java:133)
Which is pretty strange because, indeed, if you look up the source code of Hibernate it tries to do this:
#Override
#SuppressWarnings("unchecked")
public void set(Object target, Object value, SessionFactoryImplementor factory) {
( (Map) target ).put( propertyName, value ); //line 102
}
Which doesn't make any sense...
target is of type Class and this code tries to cast it to Map,
why does it try to do that???
any pointers are more than welcome...
I'm using Hibernate 5 (and am upgrading from 3)...
edit: I also use Spring (4.2.1.RELEASE; also upgrading) which calls these methods upon deploy, any debugging pointers are most welcome as well...
edit 2: (the whole FactCodeDto class, as requested)
package org.bamboomy.cjr.dto;
import org.bamboomy.cjr.model.FactCode;
import org.bamboomy.cjr.model.FactCodeType;
import org.bamboomy.cjr.utility.FullDateUtil;
import org.bamboomy.cjr.utility.Locales;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.util.Assert;
import java.util.*;
#Getter
#Setter
#ToString
public class FactCodeDto extends TreeNodeValue {
private String cdFact;
private String cdFactSuffix;
private Boolean isSupplementCode;
private Boolean isTitleCode;
private Boolean mustBeFollowed;
private Date activeFrom;
private Date activeTo;
private Boolean isCode;
private Long idFact;
private Long idParent;
private String type;
Map<Locale, String> description = new HashMap<Locale, String>(3);
public FactCodeDto() {
}
public FactCodeDto(String prefix, String suffix) {
super();
this.cdFact = prefix;
this.cdFactSuffix = suffix;
}
public FactCodeDto(String cdFact, String cdFactSuffix, Boolean isSupplementCode, Boolean mustBeFollowed) {
super();
this.cdFact = cdFact;
this.cdFactSuffix = cdFactSuffix;
this.isSupplementCode = isSupplementCode;
this.mustBeFollowed = mustBeFollowed;
}
public FactCodeDto(String cdFact, String cdFactSuffix, Boolean isSupplementCode, Boolean mustBeFollowed, Long idFact, Long idParent, Boolean isCode, Boolean isTitleCode, Date from, Date to, Map<Locale, String> descriptions,String type) {
super();
this.cdFact = cdFact;
this.cdFactSuffix = cdFactSuffix;
this.isSupplementCode = isSupplementCode;
this.mustBeFollowed = mustBeFollowed;
this.idFact = idFact;
this.idParent = idParent;
this.isCode = isCode;
this.isTitleCode = isTitleCode;
this.activeFrom = from;
this.activeTo = to;
if (descriptions != null) {
this.description = descriptions;
}
this.type = type;
}
public FactCodeDto(FactCode fc) {
this(fc.getPrefix(), fc.getSuffix(), fc.isSupplementCode(), fc.isHasMandatorySupplCodes(), fc.getId(), fc.getParent(), fc.isActualCode(), fc.isTitleCode(), fc.getActiveFrom(), fc.getActiveTo(), fc.getAllDesc(),fc.getType().getCode());
}
public String formatCode() {
return FactCode.formatCode(cdFact, cdFactSuffix);
}
public boolean isActive() {
Date now = new Date(System.currentTimeMillis());
return FullDateUtil.isBetweenDates(now, this.activeFrom, this.activeTo);
}
public void setDescFr(String s) {
description.put(Locales.FRENCH, s);
}
public void setDescNl(String s) {
description.put(Locales.DUTCH, s);
}
public void setDescDe(String s) {
description.put(Locales.GERMAN, s);
}
/**
* public String toString() {
* StringBuilder sb = new StringBuilder();
* sb.append(getIdFact() + ": ")
* .append(getIdParent() + ": ")
* .append(" " + cdFact + cdFactSuffix + ": " + (isSupplementCode ? "NO Principal " : " Principal "))
* .append((mustBeFollowed ? " Must Be Followed " : "NOT Must Be Followed "));
* return sb.toString();
* }
*/
public Map<Locale, String> getDescription() {
return description;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
String fullCode = formatCode();
result = prime * result + ((fullCode == null) ? 0 : fullCode.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;
}
FactCodeDto other = (FactCodeDto) obj;
return formatCode().equals(other.formatCode());
}
#Override
public boolean isChildOf(TreeNodeValue value) {
Assert.notNull(value);
boolean isChild = false;
if (value instanceof FactCodeDto) {
if (this.getIdParent() != null) {
isChild = this.getIdParent().equals(((FactCodeDto) value).getIdFact());
}
}
return isChild;
}
#Override
public boolean isBrotherOf(TreeNodeValue value) {
Assert.notNull(value);
boolean isBrother = false;
if (value instanceof FactCodeDto) {
if (this.getIdParent() != null) {
isBrother = this.getIdParent().equals(((FactCodeDto) value).getIdParent());
}
}
return isBrother;
}
#Override
public boolean isParentOf(TreeNodeValue value) {
Assert.notNull(value);
boolean isParent = false;
if (value instanceof FactCodeDto) {
isParent = this.getIdFact().equals(((FactCodeDto) value).getIdParent());
}
return isParent;
}
#Override
public int compareTo(TreeNodeValue to) {
if (to instanceof FactCodeDto) {
return formatCode().compareTo(((FactCodeDto) to).formatCode());
} else return 1;
}
public String getCode() {
return formatCode();
}
}
I found that AliasToBean has changed in Hibernate 5. For me adding getter for my field fixed the problem.
This exception occurs when the setters and getters are not mapped correctly to the column names.
Make sure you have the correct getters and setters for the query(Correct names and correct datatypes).
Read more about it here:
http://javahonk.com/java-lang-classcastexception-com-wfs-otc-datamodels-imagineexpirymodel-cannot-cast-java-util-map/
I do some investigation on this question. The problem is that Hibernate converts aliases for column names to upper case — cdFact becomesCDFACT.
Read for a more deeply explanation and workaround here:
mapping Hibernate query results to custom class?
In the end it wasn't so hard to find a solution,
I just created my own (custom) ResultTransformer and specified that in the setResultTransformer method:
private Query createHibernateQueryForUnmappedTypeFactDto(String sqlQuery) throws HibernateException {
return FactCodeQueries.addScalars(createSQLQuery(sqlQuery)).setResultTransformer(new FactCodeDtoResultTransformer());
//return FactCodeQueries.addScalars(createSQLQuery(sqlQuery)).setResultTransformer(Transformers.aliasToBean(FactCodeDto.class));
}
the code of the custom result transformer:
package org.bamboomy.cjr.dao.factcode;
import org.bamboomy.cjr.dto.FactCodeDto;
import java.util.Date;
import java.util.List;
/**
* Created by a162299 on 3-11-2015.
*/
public class FactCodeDtoResultTransformer implements org.hibernate.transform.ResultTransformer {
#Override
public Object transformTuple(Object[] objects, String[] strings) {
FactCodeDto result = new FactCodeDto();
for (int i = 0; i < objects.length; i++) {
setField(result, strings[i], objects[i]);
}
return result;
}
private void setField(FactCodeDto result, String string, Object object) {
if (string.equalsIgnoreCase("cdFact")) {
result.setCdFact((String) object);
} else if (string.equalsIgnoreCase("cdFactSuffix")) {
result.setCdFactSuffix((String) object);
} else if (string.equalsIgnoreCase("isSupplementCode")) {
result.setIsSupplementCode((Boolean) object);
} else if (string.equalsIgnoreCase("isTitleCode")) {
result.setIsTitleCode((Boolean) object);
} else if (string.equalsIgnoreCase("mustBeFollowed")) {
result.setMustBeFollowed((Boolean) object);
} else if (string.equalsIgnoreCase("activeFrom")) {
result.setActiveFrom((Date) object);
} else if (string.equalsIgnoreCase("activeTo")) {
result.setActiveTo((Date) object);
} else if (string.equalsIgnoreCase("descFr")) {
result.setDescFr((String) object);
} else if (string.equalsIgnoreCase("descNl")) {
result.setDescNl((String) object);
} else if (string.equalsIgnoreCase("descDe")) {
result.setDescDe((String) object);
} else if (string.equalsIgnoreCase("type")) {
result.setType((String) object);
} else if (string.equalsIgnoreCase("idFact")) {
result.setIdFact((Long) object);
} else if (string.equalsIgnoreCase("idParent")) {
result.setIdParent((Long) object);
} else if (string.equalsIgnoreCase("isCode")) {
result.setIsCode((Boolean) object);
} else {
throw new RuntimeException("unknown field");
}
}
#Override
public List transformList(List list) {
return list;
}
}
in hibernate 3 you could set Aliasses to queries but you can't do that anymore in hibernate 5 (correct me if I'm wrong) hence the aliasToBean is something you only can use when actually using aliasses; which I didn't, hence the exception.
Im my case :
=> write sql query and try to map result to Class List
=> Use "Transformers.aliasToBean"
=> get Error "cannot be cast to java.util.Map"
Solution :
=> just put \" before and after query aliases
ex:
"select first_name as \"firstName\" from test"
The problem is that Hibernate converts aliases for column names to upper case or lower case
I solved it by defining my own custom transformer as given below -
import org.hibernate.transform.BasicTransformerAdapter;
public class FluentHibernateResultTransformer extends BasicTransformerAdapter {
private static final long serialVersionUID = 6825154815776629666L;
private final Class<?> resultClass;
private NestedSetter[] setters;
public FluentHibernateResultTransformer(Class<?> resultClass) {
this.resultClass = resultClass;
}
#Override
public Object transformTuple(Object[] tuple, String[] aliases) {
createCachedSetters(resultClass, aliases);
Object result = ClassUtils.newInstance(resultClass);
for (int i = 0; i < aliases.length; i++) {
setters[i].set(result, tuple[i]);
}
return result;
}
private void createCachedSetters(Class<?> resultClass, String[] aliases) {
if (setters == null) {
setters = createSetters(resultClass, aliases);
}
}
private static NestedSetter[] createSetters(Class<?> resultClass, String[] aliases) {
NestedSetter[] result = new NestedSetter[aliases.length];
for (int i = 0; i < aliases.length; i++) {
result[i] = NestedSetter.create(resultClass, aliases[i]);
}
return result;
}
}
And used this way inside the repository method -
#Override
public List<WalletVO> getWalletRelatedData(WalletRequest walletRequest,
Set<String> requiredVariablesSet) throws GenericBusinessException {
String query = getWalletQuery(requiredVariablesSet);
try {
if (query != null && !query.isEmpty()) {
SQLQuery sqlQuery = mEntityManager.unwrap(Session.class).createSQLQuery(query);
return sqlQuery.setResultTransformer(new FluentHibernateResultTransformer(WalletVO.class))
.list();
}
} catch (Exception ex) {
exceptionThrower.throwDatabaseException(null, false);
}
return Collections.emptyList();
}
It worked perfectly !!!
Try putting Column names and field names both in capital letters.
This exception occurs when the class that you specified in the AliasToBeanResultTransformer does not have getter for the corresponding columns. Although the exception details from the hibernate are misleading.
I have problem with dojo filteringSelect and dojo combobox component. I have tried to use both components, but problem is the same. I realized that Converter is never called and I am getting error java.lang.IllegalArgumentException: argument type mismatch, when combobox is filled with values.
<xe:djFilteringSelect id="cbDocumentCategoryListtt"
dojoType="dijit.form.ComboBox"
value="#{compositeData.archiveDocument.documentCategory}"
disabled="#{javascript:!compositeData.editMode}">
<xe:this.queryExpr><![CDATA[${javascript:"*$\{0}*"}]]></xe:this.queryExpr>
<xe:this.converter>
<xp:converter
converterId="ConverterDocumentCategory">
</xp:converter>
</xe:this.converter>
<xp:selectItems id="selectItems2">
<xe:this.value><![CDATA[#{javascript:try{
ControllerDocumentCategory.getAllDocumentCategoryBasedOnLRM(compositeData.archiveDocument.entryDateFrom,compositeData.archiveDocument.entryDateUntil,compositeData.archiveDocument.fund,true)
}
catch(e)
{
var msg="Error: "+ e.toString()+" See log.";
var msgObj = new javax.faces.application.FacesMessage(javax.faces.application.FacesMessage.SEVERITY_ERROR, msg, msg);
facesContext.addMessage("",msgObj);
log.logError( e.toString(), null, e ,123,"ccArchiveDocumentBase.xsp",msg);
}}]]></xe:this.value>
</xp:selectItems>
</xe:djFilteringSelect>
And this is converter
public class ConverterDocumentCategory implements Converter{
#Override
public Object getAsObject(FacesContext context, UIComponent uiComponent, String text) {
try
{
if (text == null || text.isEmpty())
{
return null;
}
String msg=LanguageManager.getInstance().getMessageFromPropFile("_object_converter_not_found","WEB-INF/langMsg.properties");
String [] all=text.split(" - ");
String name=all[0];
String codeText=all[1];
DocumentCategory documentCategory=new DocumentCategory();
documentCategory.setCodeText(codeText);
documentCategory.setName(name);
documentCategory.setActive("");
List list=DbLotusBroker.getInstance().ftSearch(documentCategory,true);
if(list.size()==0 || list.get(0)==null)
{
FacesContext.getCurrentInstance().getCurrentInstance().addMessage("", new FacesMessage(msg));
throw new Exception(msg);
}
documentCategory=(DocumentCategory) list.get(0);
return documentCategory;
}
catch (Exception e) {
SRCLogFactory.getInstance().getLog().logError(e);
context.addMessage("", new FacesMessage("Greška:" +e));
}
return null;
}
#Override
public String getAsString(FacesContext context, UIComponent uiComponent, Object obj) {
DocumentCategory documentCategory=(DocumentCategory) obj;
return documentCategory.getName()+" - "+documentCategory.getCodeText();
}
}
And in this method I prepare list for filteringselect:
public List<SelectItem> getAllDocumentCategoryBasedOnLRM(Object objFrom, Object objUntil, Object objFund,boolean active) {
sig=new ArrayList<SelectItem>();
sigActive=new ArrayList<SelectItem>();
FacesContext context=FacesContext.getCurrentInstance();
try
{
if(objFrom==null || objFund == null || objUntil==null || ((Fund)objFund).getCodeText()==null || ((Fund)objFund).getCodeText().equals(""))
{
return sig;
}
Fund fund=(Fund) objFund;
Date date=(Date) objFrom;
Calendar cal=Calendar.getInstance();
cal.setTime(date);
if(fund.getCodeText().equals("3")){
LRM lrmm= new LRM();
lrmm.setCurrent("1");
lrmm.setFund(fund);
lrmm=(LRM) DbLotusBroker.getInstance().ftSearch(lrmm, true).get(0);
if(cal.after(lrmm.getDateFrom()) || cal.equals(lrmm.getDateFrom()))
{
DocumentCategory dc=new DocumentCategory();
LRMGroup lrmGroup = new LRMGroup();
lrmGroup.setLrm(lrmm);
dc.setLrmGroup(lrmGroup);
dc.setActive("1");
List<Object>list=DbLotusBroker.getInstance().ftSearch(dc, true);
sig.add(new SelectItem(new DocumentCategory()));
sigActive.add(new SelectItem(new DocumentCategory()));
for (Object object : list)
{
DocumentCategory documentCategory=(DocumentCategory) object;
sig.add(new SelectItem(documentCategory));
if(documentCategory.getActive().equals("1"))
sigActive.add(new SelectItem(documentCategory));
}
}else{
getLrmCategory(objFrom, objUntil, objFund, active);
}
}
else{
getLrmCategory(objFrom, objUntil, objFund, active);
}
}
catch (Exception e) {
SRCLogFactory.getInstance().getLog().logError(e);
context.addMessage("", new FacesMessage("Greška "+e));
}
if(active)
return sigActive;
else
return sig;
}
Converter is registered in faces-config.xml file:
<description>A Converter for document category</description>
<converter-id>ConverterDocumentCategory</converter-id>
<converter-class>com.src.converters.ConverterDocumentCategory</converter-class>
</converter>
I think that problem is caused by using component from extension library xe:djFilteringSelect and maybe this component can't work with Converter. Any ideas?
I am a newbie for Richfaces components. When I am using the <rich:listShuttle>, the Arraylist specified in the targetValue is now getting updated with the latest data?
MyJSF File
<a4j:region>
<rich:listShuttle sourceValue="#{bean.selectItems}" id="one"
targetValue="#{bean.selectItemsone}" var="items" listsHeight="150"
sourceListWidth="130" targetListWidth="130"
sourceCaptionLabel="Intial Items"
targetCaptionLabel="Selected Items" converter="Listconverter">
<rich:column>
<h:outputText value="#{items.value}"></h:outputText>
</rich:column>
</rich:listShuttle>
</a4j:region>
<a4j:region>
<a4j:commandButton value="Submit" action="#{bean.action}" />
</a4j:region>
My Managed Bean
private List<String> selectedData;
private List<BeanItems> selectItems;
private List<BeanItems> selectItemsone;
public String action() {
System.out.println(selectItems);
System.out.println(selectItemsone);
System.out.println("Select Item List");
Iterator<BeanItems> iterator = selectItems.iterator();
while (iterator.hasNext()) {
BeanItems item = (BeanItems) iterator.next();
System.out.println(item.getValue());
}
System.out.println("/nSelect Item one list ");
Iterator<BeanItems> iterator2 = selectItemsone.iterator();
while (iterator2.hasNext()) {
BeanItems item = (BeanItems) iterator2.next();
System.out.println(item.getValue());
}
return "";
}
public void setSelectedData(List<String> selectedData) {
this.selectedData = selectedData;
}
public List<String> getSelectedData() {
return selectedData;
}
/**
* #return the selectItems
*/
public List<BeanItems> getSelectItems() {
if (selectItems == null) {
selectItems = new ArrayList<BeanItems>();
selectItems.add(new BeanItems("value4", "label4"));
selectItems.add(new BeanItems("value5", "label5"));
selectItems.add(new BeanItems("value6", "label6"));
selectItems.add(new BeanItems("value7", "label7"));
selectItems.add(new BeanItems("value8", "label8"));
selectItems.add(new BeanItems("value9", "label9"));
selectItems.add(new BeanItems("value10", "label10"));
}
return selectItems;
}
/**
* #return the selectItemsone
*/
public List<BeanItems> getSelectItemsone() {
if (selectItemsone == null) {
selectItemsone = new ArrayList<BeanItems>();
selectItemsone.add(new BeanItems("value1", "label1"));
selectItemsone.add(new BeanItems("value2", "label2"));
selectItemsone.add(new BeanItems("value3", "label3"));
}
return selectItemsone;
}
My Converter Class
public Object getAsObject(FacesContext context, UIComponent component,String value) {
int index = value.indexOf(':');
return new BeanItems(value.substring(0, index), value.substring(index + 1));
}
public String getAsString(FacesContext context, UIComponent component,Object value) {
BeanItems beanItems = (BeanItems) value;
return beanItems.getValue() + ":" + beanItems.getData();
}
My BeanItems Class
private String data; //Getter & setter
private String value; //Getter & setter
public BeanItems() {
}
public BeanItems(String value, String data) {
this.value = value;
this.data = data;
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((data == null) ? 0 : data.hashCode());
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final BeanItems other = (BeanItems) obj;
if (data == null) {
if (other.data != null)
return false;
} else if (!data.equals(other.data))
return false;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
If your question is that the target list is not getitng populated then i think you are supposed to override equals , hash code method for the wrapper object[BeanItem] since in the converter you are constructing new object every time in getAsObject method.
Also try putting a h:message tag wrapped in --a4j:outputPanel ajaxRendered="true"-- in your page to print any conversion errors that might be getting generated.