AutoMapper how to resolve using calculated fields - automapper

How does one perform Business Logic along with AutoMapper?
e.g I have a field named "CalculatedField" and it is calculated from FirstName + LastName plus their Email.
example of the code
if (firstName != null)
CalculatedField += firstName;
if (lastName != null)
CalculatedField += lastName;
if (email != null)
CalculatedField += email;

Related

How to build a query with Panache which depends on optional fields?

Let's say I have the following database entity:
Customer
- firstname
- lastname
- status (active | inactive)
- email
And the following (simplified) Java method:
getCustomers (String firstname, String lastname, String status, String email) {...}
If all strings are empty I want to retrieve all records from the database. If lastname and status have values I want to retrieve the relevant records. And so on. So when creating the query I need to check if the fields have values and then add these fields to the database query. How would I best implement this with Quarkus Panache?
I don't think there is a specific method to do it but if you pass the parameters as Map<String, Object>:
public List<Customer> getCustomers(Map<String, Object> parameters)
if ( parameters == null ) {
return Customer.listAll();
}
Map<String, Object> nonNullParams = parameters.entrySet().stream()
.filter( entry -> entry.getValue() != null )
.collect( Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue ) );
if ( nonNullParams.isEmtpty() ) {
return Customer.listAll();
}
String query = nonNullParams.entrySet().stream()
.map( entry -> entry.getKey() + "=:" + entry.getKey() )
.collect( Collectors.joining(" and ") );
return Customer.find(query, nonNullParams);
}
or
public List<Customer> getCustomers (String firstname, String lastname, String status, String email) {
Map<String, Object> parameters = new HashMap<>();
addIfNotNull(parameters, "firstName", firstName );
addIfNotNull(parameters, "lastName", lastName );
addIfNotNull(parameters, "status", status );
addIfNotNull(parameters, "email", email );
if ( parameters.isEmtpty() ) {
return Customer.listAll();
}
String query = parameters.entrySet().stream()
.map( entry -> entry.getKey() + "=:" + entry.getKey() )
.collect( Collectors.joining(" and ") );
return Customer.find(query, parameters)
}
private static void addIfNotNull(Map<String, Object> map, String key, String value) {
if (value != null) {
map.put(key, value);
}
}
You can use filters for this, which is a Hibernate feature supported by Panache. It's not very well documented, so here is a working example.
Your database entity:
#Entity
#Table(name = "book")
#FilterDef(name = "Books.byISBN",
defaultCondition = "isbn = :isbn",
parameters = #ParamDef(isbn = "isbn", type = "string"))
#Filter(name = "Books.byISBN")
public class Book extends PanacheBaseEntity {}
Your web resource
#Path("book")
public class BookResource {
#Inject
BookRepository bookRepository;
#GET
#Produces(MediaType.TEXT_PLAIN)
public String index(#QueryParam("isbn") Optional<String> isbnFilter) {
final PanacheQuery<Book> bookQuery = bookRepository.findAll();
isbnFilter.ifPresent(isbn -> bookQuery.filter("Book.byISBN", Parameters.with("isbn", isbn)));
return bookQuery.list().stream()
.map(Book::name)
.collect(Collectors.joining(","));
}
}

Constructor Expression in TypedQuery with CriteriaQuery

I was using JPQL NamedQueries earlier in order to get some values from one db-table A and put it into PartsOfA via a constructor expression like this: SELECT new ...PartsOfA(a.member) from A a where ...
Now I am trying to do the same with a TypedQuery which shall be created by using a CriteriaQuery.
Is it possible? If yes, how?
Yes you can.
Example:
TransferObject:
public class BasicTeacherInfo {
private String firstName;
private String lastName;
public BasicTeacherInfo(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
Query
CriteriaQuery<BasicTeacherInfo> query = cb.createQuery(BasicTeacherInfo.class);
Root<Teacher> teacher = query.from(Teacher.class);
query.multiselect(teacher.get("firstName"),teacher.get("lastName"));
List<BasicTeacherInfo> results = em.createQuery(query).getResultList();
for (BasicTeacherInfo info : results) {
System.out.println("First name = " + info.getFirstName() + " "
+ "Last Name = " + info.getLastName());
}
examples from http://www.thejavageek.com/2014/04/27/multiselect-criteria-api/
By the way: You seem to misuse TypedQuery word. A Criteria Query is not a TypedQuery. TypedQuery<T> is the Type of e.g. a named query.

Inserting 3 fields into dictionary and how to retrieve them

I am trying to insert three fields into dictionary, so converted two fields into object type and added to the dictionary
int empId;
String empName;
decimal salary;
public EmployeeClass(string empName,decimal salary)
{
this.empName = empName;
this.salary = salary;
}
//creating dictionary object
Dictionary<int, object> employeeDictionary = new Dictionary<int, object>();
EmployeeClass emp;
for(int i=0;i<5;i++)
{
Console.WriteLine("Enter Employee " + (i+1) + " details ");
Console.WriteLine("Enter Employee ID ");
empId = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Enter Employee Name ");
empName = Console.ReadLine();
Console.WriteLine("Enter Salary of the employee ");
salary= Convert.ToDecimal(Console.ReadLine());
//creating object for two fields
emp= new EmployeeClass(empName,salary);
//adding to dictionary one is field and other object
employeeDictionary.Add(empId, emp);
}
Now I am trying to retrieve the details from the dictionary:
var list = employeeDictionary.Keys.ToList();
list.Sort();
Console.WriteLine("The Employee details according to EmpID is ");
foreach(var temp in list)
{
Console.WriteLine(temp + " " + employeeDictionary[temp].empName + " " + employeeDictionary[temp].salary);
}
but I am getting this error:
Object does not contain definition for empName and no extension method empName accepting a first argument of type Object
How to retrieve the details from dictionary when an object type is given?
1. Private vs public fields
Your first problem is that your properties (empName and salary) are private fields of EmployeeClass:
public class EmployeeClass
{
int empId;
String empName;
decimal salary;
...
}
You don't use an explicit access modifier, so per default your fields are private. Change it to this:
public class EmployeeClass
{
public int empId {get; private set;}
public String empName {get; private set;}
public decimal salary {get; private set;}
...
}
2. Use the correct type
Why are you using a Dictionary<int,object> if you want to store instances of EmployeeClass? Simply use a
Dictionary<int,EmployeeClass> employeeDirectory
So the compiler will know that the "object" in the dictionary is a EmployeeClass and will know it's properties. If you use object, the compiler has no way to know that there are those properties.

<f:selectItems> only shows toString() of the model as item label

I am facing a problem to populate SelectOneMenu correctly using converter. I followed several google references and lastly http://balusc.blogspot.in/2007/09/objects-in-hselectonemenu.html link of BalusC. But could not figure out the problem. I am giving all my entity, managedbean and jsf code for your consideration.
Entity:
#Entity
#Table(name = "gender")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Gender.findAll", query = "SELECT g FROM Gender g"),
#NamedQuery(name = "Gender.findById", query = "SELECT g FROM Gender g WHERE g.id = :id"),
#NamedQuery(name = "Gender.findByGender", query = "SELECT g FROM Gender g WHERE g.gender = :gender")})
public class Gender implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "id")
private Short id;
#Size(max = 10)
#Column(name = "gender")
private String gender;
#OneToMany(mappedBy = "gender")
private List<Patient> patientList;
public Gender() {
}
public Gender(Short id) {
this.id = id;
}
public Short getId() {
return id;
}
public void setId(Short id) {
this.id = id;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
#XmlTransient
public List<Patient> getPatientList() {
return patientList;
}
public void setPatientList(List<Patient> patientList) {
this.patientList = patientList;
}
#Override
public int hashCode() {
return (id != null) ? (getClass().hashCode() + id.hashCode()) : super.hashCode();
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
return (object instanceof Gender) && (id != null) ? id.equals(((Gender) object).id) : (object == this);
}
#Override
public String toString() {
return "hms.models.Gender[ id=" + id + " ]";
}
Converter:
#FacesConverter(value = "genderConverter")
public class GenderConverter implements Converter {
#Inject GenderService gs;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if(value == null){
return null;
}
return gs.getGender(Integer.parseInt(value));
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if(value == null) {
return null;
}
if (!(value instanceof Gender)) {
throw new ConverterException("The value is not a valid Gender: " + value);
}
return ((Gender) value).getGender();
}
}
And JSF SelectOneMenu tag:
<h:selectOneMenu id="gender" value="#{registrationBean.patient.gender}" converter="genderConverter">
<f:selectItem itemValue="0" itemLabel="-- Select Gender --" />
<f:selectItems value="#{registrationBean.genderList}" />
</h:selectOneMenu>
The dropdown output is similar to "hms.models.Gender[id=1]" and so on. Image sample has given below:
Can anybody help? Thanks in advance.
Your problem is two-fold.
Firstly, in the output the select item label is being presented, not the select item value. The converter only affects the select item value, not the select item label. Unless otherwise specified via itemLabel attribute, the select item label defaults to toString() outcome of the select item value.
Assuming that you want to display the gender property as select item label, then this should do:
<f:selectItems value="#{registrationBean.genderList}" var="gender"
itemValue="#{gender}" itemLabel="#{gender.gender}" />
(it should be said that having a property with the same name as the class itself is quite awkward)
That should fix the presentation fail.
Secondly, you're for the output converting from Gender instance to its gender property. However, you're for the input attempting to interpret the outputted gender property as an id in order to find the associated Gender instance. This doesn't make any sense. Fix the converter accordingly to give the id back in getAsString(), exactly as expected by getAsObject().
return ((Gender) value).getId().toString();
That should fix the form submit fail.
See also:
How to populate options of h:selectOneMenu from database?

Dynamic JSF converter

The data access layer behind my JSF app uses two different formats for date fields (sometimes ddmmyyyy, sometimes yyyymmdd); the displayed date is always dd/mm/yyyy.
Is there a way to use two different converters for a single field and decide which one to use dynamically? Like "if this command button is clicked, use this converter, else if this other command button is clicked, use that converter".
BalusC explains why this is difficult here: https://stackoverflow.com/a/7123153/3284943
Based on his suggestions, I created this:
in the JSF file, I use my custom converter and its defined attributes, to select the converter and dynamic attributes at runtime. This is a snippet from a dynamic PrimeFaces DataTable:
<h:outputText rendered = "#{column.dateTime}" value="#{table[column.name]}">
<f:converter converterId="runtimeConverterSelector" />
<f:attribute name="converterId" value="#{column.converterName}" />
<f:attribute name="pattern" value="#{column.converterPattern}" />
</h:outputText>
The custom converter:
#FacesConverter ("runtimeConverterSelector")
public class RuntimeConverterSelector implements Converter {
Converter delegateConverter;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
retrieveAttributes(component);
return delegateConverter.getAsObject(context, component, value);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
retrieveAttributes(component);
return delegateConverter.getAsString(context, component, value);
}
private void retrieveAttributes(UIComponent component) {
Object attribute;
attribute = component.getAttributes().get("converterId");
if (attribute != null) {
String converterName = (String)attribute;
switch (converterName) {
case "javax.faces.BigDecimal":
delegateConverter = new BigDecimalConverter();
break;
case "javax.faces.BigInteger":
delegateConverter = new BigIntegerConverter();
break;
case "javax.faces.Boolean":
delegateConverter = new BooleanConverter();
break;
case "javax.faces.Byte":
delegateConverter = new ByteConverter();
break;
case "javax.faces.Character":
delegateConverter = new CharacterConverter();
break;
case "javax.faces.DateTimeConverter":
delegateConverter = new DateTimeConverter();
attribute = component.getAttributes().get("pattern");
if (attribute != null) ((DateTimeConverter)delegateConverter).setPattern((String)attribute);
attribute = component.getAttributes().get("timeZone");
if (attribute != null) ((DateTimeConverter)delegateConverter).setTimeZone(TimeZone.getTimeZone((String)attribute));
attribute = component.getAttributes().get("dateStyle");
if (attribute != null) ((DateTimeConverter)delegateConverter).setDateStyle((String)attribute);
attribute = component.getAttributes().get("timeStyle");
if (attribute != null) ((DateTimeConverter)delegateConverter).setDateStyle((String)attribute);
attribute = component.getAttributes().get("type");
if (attribute != null) ((DateTimeConverter)delegateConverter).setDateStyle((String)attribute);
break;
case "javax.faces.Double":
delegateConverter = new DoubleConverter();
break;
case "javax.faces.Enum":
delegateConverter = new EnumConverter();
break;
case "javax.faces.Float":
delegateConverter = new FloatConverter();
break;
case "javax.faces.Integer":
delegateConverter = new IntegerConverter();
break;
case "javax.faces.Long":
delegateConverter = new LongConverter();
break;
case "javax.faces.Number":
delegateConverter = new NumberConverter();
attribute = component.getAttributes().get("currencyCode");
if (attribute != null) ((NumberConverter)delegateConverter).setCurrencyCode((String)attribute);
attribute = component.getAttributes().get("currencySymbol");
if (attribute != null) ((NumberConverter)delegateConverter).setCurrencySymbol((String)attribute);
attribute = component.getAttributes().get("groupingUsed");
if (attribute != null) ((NumberConverter)delegateConverter).setGroupingUsed(Boolean.parseBoolean((String)attribute));
attribute = component.getAttributes().get("integerOnly");
if (attribute != null) ((NumberConverter)delegateConverter).setIntegerOnly(Boolean.parseBoolean((String)attribute));
attribute = component.getAttributes().get("locale");
if (attribute != null) ((NumberConverter)delegateConverter).setLocale(new Locale((String)attribute));
attribute = component.getAttributes().get("maxFractionDigits");
if (attribute != null) ((NumberConverter)delegateConverter).setMaxFractionDigits(Integer.parseInt((String)attribute));
attribute = component.getAttributes().get("maxIntegerDigits");
if (attribute != null) ((NumberConverter)delegateConverter).setMaxIntegerDigits(Integer.parseInt((String)attribute));
attribute = component.getAttributes().get("minFractionDigits");
if (attribute != null) ((NumberConverter)delegateConverter).setMinFractionDigits(Integer.parseInt((String)attribute));
attribute = component.getAttributes().get("minIntegerDigits");
if (attribute != null) ((NumberConverter)delegateConverter).setMinIntegerDigits(Integer.parseInt((String)attribute));
attribute = component.getAttributes().get("pattern");
if (attribute != null) ((NumberConverter)delegateConverter).setPattern((String)attribute);
attribute = component.getAttributes().get("type");
if (attribute != null) ((NumberConverter)delegateConverter).setType((String)attribute);
break;
case "javax.faces.Short":
delegateConverter = new ShortConverter();
break;
default:
System.err.println("ConverterId provided to runtimeConverterSelector, '" + converterName + "' is invalid. The default converter will be used");
break;
}
} else {
System.err.println("No converterId was provided to runtimeConverterSelector. The default converter will be used.");
}
}
}
AFAIK it's not possible. But you can check for both formats within one converter. Of course, conversion to string will be done basing to one format. Alternatively, you can format date basing on the current Locale. Basic example:
#FacesConverter("doubleDateConverter")
public class DateConverter implements Converter {
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if(value == null || value.equals("")) {
return null;
}
SimpleDateFormat format1 = new SimpleDateFormat("ddMMyyyy");
SimpleDateFormat format2 = new SimpleDateFormat("yyyyMMdd");
Date date = null;
boolean valid = true;
try {
date = format1.parse(value);
} catch (ParseException e) {
valid = false;
}
if(!valid) {
try {
date = format2.parse(value);
} catch (ParseException e) {
valid = false;
}
}
if((!valid) || (date == null)) {
throw new ConverterException(new FacesMessage("Date is in wrong format: " + value));
}
return date;
}
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (!(value instanceof Date) || (value == null)) {
return null;
}
SimpleDateFormat format = new SimpleDateFormat("ddMMyyyy");
return format.format((Date)value);
}
}

Resources