I have a lots of classes that extends from one class
Also I have one method that its argument is that parent class and create query base on attribute of those classes.
sometimes I need to ignore some attribute from result query.
so is it possible to remove some attribute of object?
class A1 extends Model {
public String field1 = "";
public String field2 = "";
public String table = "A1";
#Override
public String getTable() {
return this.table;
}
}
class A2 extends Model {
public String field1 = "";
public String field2 = "";
public String field3 = "";
public String table = "A2";
#Override
public String getTable() {
return this.table;
}
}
class Utility {
public static String query(Model params) {
Field[] fields = params.getClass().getFields();
String head = "INSERT INTO " + params.getTable() + "(";
String tail = "VALUES (";
for(Field field : fields) {
String key = field.getName();
String val;
try {
val = field.get(params);
} catch (Exception e) {
val = null;
}
head += key + ",";
tail += "'" + val + "',";
}
head = head.substring(head,0,head.length() -1) + ")";
tail = tail.substring(tail,0,tail.length() -1) + ")";
return head + tail;
}
}
I call query method by sending one model
A1 data = new A1();
data.field1 = "Name";
data.field2 = "Family";
String query = Utility.query(data);
I just want to remove field2 from query how can I do that?
thanks for any help
You could implement an annotiation. Let's call it #DontPersist. Use it to mark fields which should not get persisted. In Utility.query() you can check for the annotation with reflection.
As your Model class does not implement anything (it could be an interface, but that's another topic), you can extend it creating a class with less attributes when necessary (an anonymous class will do the job).
Anyway, I think you should refactor your code: why not using a List to store fields? It's easier and it does not need reflection.
I'll use something like:
public class Model extends ArrayList{
public Model(String name) { tableName=name;}
private String tableName;
public String getTable() {return tableName}
}
And then you can iterate over the Array to obtain the field names.
Related
(This is just a question out of convenience. Right now I'm doing it manually, but ideally, there should be a library that already takes care of this, but I just couldn't find any.)
Basically, I'd like to take two objects A and B of the same class (same one, no inheritance) and create a new one with just the differences that were introduced in B for every field, keeping the remaining fields set to null. Of course I could just manually call every single getter and do an equals, then set the field, but that would be a lot of boilerplate.
So far I was only able to find solutions for comparing two objects, as in, all that would be returned is a boolean. But that wouldn't take away the problem of still having to manually check every single field. I had something like this in mind:
class MyPojo {
private String field1;
private String field2;
private String field3;
// ... getters and setters here ...
}
MyPojo oldObj = new MyPojo("a", "b", "c");
MyPojo newObj = new MyPojo("a", "b", "x");
MyPojo diff = createDifferenceObject(oldObj, newObj);
assert(diff.getField1() == null);
assert(diff.getField2() == null);
assert(diff.getField3() == "x");
public MyPojo createDifferenceObject(MyPojo oldObj, MyPojo newObj) {
return TheMagicLibraryIAmLookingFor.createDifferenceView(oldObj, newObj);
}
You could use reflection.
Sample input
MyPojo oldObj = new MyPojo("a", "b", "c",true,5);
MyPojo newObj = new MyPojo("a", "b", "x",true,3);
Output: Creates a new difference object:
MyPojo{field1='null', field2='null', field3='x', field4=null, field5=3}
Rough idea as a starter using a map of field names to their values:
(This needs to be tested and add error checking)
public class CompareFields {
public static void main(String[] args) throws IllegalAccessException {
MyPojo oldObj = new MyPojo("a", "b", "c",true,5);
MyPojo newObj = new MyPojo("a", "b", "x",true,3);
Map<String, Object> mapping = mapDeltas(oldObj,newObj);
System.out.println(mapping);
// now map to constructor / properties etc....
MyPojo newDeltas = createNewObj(mapDeltas(oldObj, newObj));
System.out.println(newDeltas);
}
private static Map<String, Object> mapDeltas(MyPojo oldObj, MyPojo newObj) throws IllegalAccessException {
HashMap<String, Object> mapFieldNamesToValues= new HashMap<>();
Field[] fieldsA = oldObj.getClass().getDeclaredFields();
Field[] fieldsB = newObj.getClass().getDeclaredFields();
// add type and name checking etc...
for (int i = 0; i < fieldsA.length && i<fieldsB.length; i++) {
Field fieldA = fieldsA[i];
Field fieldB = fieldsB[i];
fieldA.setAccessible(true);
fieldB.setAccessible(true);
if(fieldA.get(oldObj).equals(fieldB.get(newObj))){
mapFieldNamesToValues.put(fieldA.getName(), null);
}else{
mapFieldNamesToValues.put(fieldB.getName(),fieldB.get(newObj));
}
}
return mapFieldNamesToValues;
}
private static MyPojo createNewObj(Map<String, Object> mapDeltas) throws IllegalAccessException {
MyPojo newObj = new MyPojo();
Field[] fields = newObj.getClass().getDeclaredFields();
// add type and name checking etc...
for (int i = 0; i < fields.length ; i++) {
Field field = fields[i];
field.setAccessible(true);
Object value = mapDeltas.get(field.getName());
if(value!=null){
field.set(newObj, value);
}
}
return newObj;
}
}
And the POJO:
public class MyPojo {
private String field1;
private String field2;
private String field3;
private Boolean field4;
private Integer field5;
public MyPojo(String field1, String field2, String field3, Boolean field4, Integer field5) {
this.field1 = field1;
this.field2 = field2;
this.field3 = field3;
this.field4 = field4;
this.field5 = field5;
}
public MyPojo() {
}
#Override
public String toString() {
return "MyPojo{" +
"field1='" + field1 + '\'' +
", field2='" + field2 + '\'' +
", field3='" + field3 + '\'' +
", field4=" + field4 +
", field5=" + field5 +
'}';
}
}
Or this library may help Apache Commons Lang API
More generally avoiding reflection it may be that if only a few fields change then this is an indication that you need to take another look at those POJO designs and look at separating those areas that frequently change from those that don't and maybe compose objects on that basis in a different way.
We can use Java Reflection API to compare two objects as below. You can use the 2nd option if you have Boolean or Integer fields.
public Object createDifferenceView(Object one, Object two) throws Exception{
Object result = two.getClass().newInstance();
Method[] methods = one.getClass().getDeclaredMethods();
for (Method m: methods) {
if(m.getName().startsWith("get")) {
String methodName = m.getName();
methodName = "s"+methodName.substring(1, methodName.length());
if(m.invoke(one).equals(m.invoke(two))) {
two.getClass().getMethod(methodName, String.class).invoke(result, (Object)null);
}else {
two.getClass().getMethod(methodName, String.class).invoke(result, m.invoke(two));
}
}
}
return result;
}
OR
public Object createDifferenceView1(Object one, Object two) throws Exception {
Object result = two.getClass().newInstance();
Field[] fields = one.getClass().getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
if(f.get(one).equals(f.get(two)))
f.set(result, null);
else
f.set(result, f.get(two));
}
return result;
}
I have some classes like below:
#Getter
#Setter
class Person{
#JsonProperty("cInfo")
private ContactInformation contactInfo;
private String name;
private String position;
}
#Getter
#Setter
class ContactInformation{
#JsonProperty("pAddress")
private Address address;
}
#Getter
#Setter
class Address{
private String street;
private String district;
}
And what I am going to do is writing an Utils method for the Person object that take one parameter which is the attributeName as String and return the getter value for this attribute.
Ex:
attributeName = name -> return person.getName()
attributeName = position -> return person.getPosition()
attributeName = cInfo.pAddress.street -> return person.getContactInfo().getAddress().getStreet()
attributeName = cInfo.pAddress.district -> return person.getContactInfo().getAddress().getDistrict()
Below is what I've done: I loop through all the fields in the Person object and check if the attributeName equal to either the JsonProperty's Name or the Field's Name then I will return this getter.
Object result;
Field[] fields = Person.class.getDeclaredFields();
for (Field field : fields) {
JsonProperty jsonProperty = field.getDeclaredAnnotation(JsonProperty.class);
if (jsonProperty != null && jsonProperty.value().equals(attributeName)) {
result = Person.class.getMethod("get" + capitalize(field.getName())).invoke(person);
} else {
if (field.getName().equals(attributeName)) {
result = person.class.getMethod("get" + capitalize(field.getName()))
.invoke(person);
}
}
}
This worked but only with the fields that locate direct in the Person class, ex: name, position. With the fields inside of contactInfo or address I am still getting stuck there. Can anyone give me some hint here how can I do it?
Thank you!
Because path like a.b.c related to different objects. So you need to. split by point and for each token call get and use obtained result for next token
UPDATE: something like:
private static Object invkGen(Object passedObj, String attributeName) throws Exception {
final String[] split = attributeName.split("\\.");
Object result = passedObj;
for (String s : split) {
if (result == null) {
break;
}
result = invk(result, s);
}
return result;
}
private static Object invk(Object passedObj, String attributeName) throws Exception {
Object result = null;
final Field[] fields = passedObj.getClass().getDeclaredFields();
for (Field field : fields) {
JsonProperty jsonProperty = field.getDeclaredAnnotation(JsonProperty.class);
if (jsonProperty != null && jsonProperty.value().equals(attributeName)) {
result = Person.class.getMethod("get" + capitalize(field.getName())).invoke(passedObj);
} else {
if (field.getName().equals(attributeName)) {
result = passedObj.getClass().getMethod("get" + capitalize(field.getName()))
.invoke(passedObj);
}
}
}
return result;
}
I am working on ETL Java project and it does 3 things
extract - read the data from a table
transform the data to JSON
Load the data
It works fine. The issue is I am doing it for each table. The way I have right now is
class ETLHelper
{
private Person read(ResultSet results){
Person p = new Person();
p.setPersonId(results.getString("PERSON_ID"));
p.setPersonName(results.getString("PERSON_NAME"));
return p;
}
private String transform(Person p){
TransformPerson t = new TransformPerson();
t.setTransformPersonId(p.getPersonId);
t.setTransformPersonName(p.getPersonName);
PersonEData eData = new PersonEData();
eData.setDate1(p.date1);
eData.setDate2(p.date2);
t.seteData(eData);
PersonDetails pd = new PersonDetails();
pd.settransformdata(t);
return writeValueAsString(pd);
}
public void etl(){
Connection c = null;
PreparedStatement p = null;
ResultSet r = null;
c = getConnection();
p = c.prepareStatement(getSql());
r = p.executeQuery();
while(r.next()){
messages.add(transform(read(r)));
/*code for loading data*/
}
}
}
Person.Java:
#JsonTypeName(value = "PERSON")
#JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
public class Person{
#JsonProperty(value = "PERSON_ID")
private String personId;
//getter and setter for personId
#JsonProperty(value = "PERSON_NAME")
private String personName;
//getter and setter for personName
}
TransformPerson.java:
#JsonRootName(value = "Person")
class TransformPerson{
private String transformPersonName;
private String transformPersonId;
/*getter and setter for transformPersonName and tranformPersonId*/
#override
String toString(){
return "Person [name =" + transformPersonName + ", id = " + transformPeronId "]";
}
}
PersonEdata:
private String date1;
private String date2;
/*getter and setter*/
#override
public String toString(){
return "PersonEdata [date1=" + date1 +", date2=" + date2 + "]";
}
So a Person class, a class needed for transformation and etl class is written for each table. There are also some additional classes like PersonEdata that returns JSON when toString() is called. Is there anyway can I change this design to avoid writing the similar code for each table? There are some constraints. Each table is different and they transformation class is needed because there are other programs that uses the JSON generated so we need to generate JSON that needs to understood by those programs.
In your current solution, you have created :
Person class - to hold the data retrieved from DB
PersonTransform class - to copy the data from Person to other representation and have extended the capability to create JSON by overrinding toString()
To keep it simple what you can do is:
Have single Class like Person for each entity (Table) - which is JSON Serializable.
Don't override the toString method to represent the JSON representation - use JSON serializer instead.
I have a string which looks like this and it represents a pojo.
Model [Name=Mobie , location= US, actualTransferDate=null, scanserialCode=234335,1237787, modelNum=MIC 898989 ]
I want bit clearer to reader on the above string. I want to read the user checked checkbox values(represents entire row with the fileds in below pojo) in an jsp page table to another jsp page. So, in the controller i read these checked checkbox rows as bellow.
String[] checkeditems = request.getParameterValues("case");//case represents the entire row
for (String string : checkeditems) {
log.info("row1"+string);// String pasted above in the message
}
From the above it returns as a string Array which i want convert to be as a list object, so that i can easily send this list to next jsp for a view. I feel i am heading to wrong direction and doing some unrelated stuff.
I have a pojo as
public class Model{
private String Name;
private String location;
private String actualTransferDate;
private String scanserialCode;
private String modelNum;
====Getters/Setter======
How i can convert this String to this model object?
you can split the string on ", " and iterate over the result array. With BeanUtils from apache can you fill your new pojo instance.
Example:
public class Model {
private String Name;
private String location;
private String actualTransferDate;
private String scanserialCode;
private String modelNum;
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getActualTransferDate() {
return actualTransferDate;
}
public void setActualTransferDate(String actualTransferDate) {
this.actualTransferDate = actualTransferDate;
}
public String getScanserialCode() {
return scanserialCode;
}
public void setScanserialCode(String scanserialCode) {
this.scanserialCode = scanserialCode;
}
public String getModelNum() {
return modelNum;
}
public void setModelNum(String modelNum) {
this.modelNum = modelNum;
}
#Override
public String toString() {
return "[Name = " + getName() + "location = " +getLocation() + ", actualTransferDate = " + getActualTransferDate() + ", scanserialCode = " + getScanserialCode() + ", modelNum = " + getModelNum() + "]";
}
}
import org.apache.commons.beanutils.BeanUtils;
public class Main {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException {
String model = new String("Name=Mobie , location= US, actualTransferDate=null, scanserialCode=234335,1237787, modelNum=MIC 898989");
String[] modelValues = model.split(", ");
Model m = new Model();
for (String value : modelValues) {
String[] s = value.split("=");
String fieldName = s[0];
String fieldValue = s[1];
BeanUtils.setProperty(m, fieldName, fieldValue);
}
System.out.println(m.toString());
}
}
Maven dependency:
<dependencies>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>
</dependencies>
If you want it completely dynamic, you can use Reflection.
For example, use a regular expression (Pattern/Matcher) to find the [ ... ] part, use the String before that as a class name (assuming you know the package name) and then do a simple comma/equals-sign split in the [ ... ] part and fill the fields via reflection... Not that hard to do.
You can define a constructor in the Model class which accepts the full string as input. The use StringTokenizer with delimiter as ',' to convert the string to a list of tokens. Then tokenizer each token with '='as the delimiter. This way you will have all the members of Model class tokens which can be used to initialize the values of the member variables.
When I was doing hashmap coding, I hit a bump to identify key, an example as follow:
public void addBE(BookEntry entry)
{
library.put(entry.getBname().getTitle(), entry);
}
So for this example, is getBname() or getTitle the key for this?, and entry should be the value right? But entry represents a class, so it means everything in entry is the value?
Thanks for clarifing my doubt.
FYI: BookEntry class:
public class BookEntry{
private BookName bname;
private Writer wname;
private BookID b_id;
public BookEntry(BookName bname, Writer wname, BookID b_id)
{
this.bname = bname;
this.wname = wname;
this.b_id = b_id;
}
public BookName getBname()
{
return bname;
}
public Writer getWname()
{
return wname;
}
public BookID getBookID()
{
return b_id;
}
public String toString()
{
return bname.toString() + " " + wname.toString() + " " + b_id.toString();
}
public static BookEntry enterBE()
{
BookName bname = BookName.enterName();
Writer wname = Writer.enterWriter();
BookID b_id = BookID.enterID();
return new BookEntry(bname, wname, b_id);
}
}
The key is the title, i.e. the output of getTitle(). It would be the same as:
String titleKey = entry.getBname().getTitle(); // assuming title is a String, but it could be any type
library.put(titleKey, entry);
Yes, your understanding is correct. The whole entry class is a value.
In the below line
library.put(entry.getBname().getTitle(), entry);
entry.getBname().getTitle() --> key
entry ---> value.
In Java Map<Key,Value>.
Now here
library.put(entry.getBname().getTitle(), entry); // library is your Map
And what ever the value return from entry.getBname().getTitle() is the Key and entry is the value.