Did you ever wanted to implement simple mapping of database tables to java objects (models) and you wanted to do the whole stuff dynamically? I managed to put some code together which does exactly what you wanted
At first I created a model class in Java reflecting one database table. I used annotations to specify that the class is an Entity and the table fields modeled in the class are Columns. These annotations are available in Java SDK packages javax.persistence.Column and javax.persistence.Entity. But I also wanted to play with annotations so I created them myself:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String name() default "";
}
As an example of database table/java model I’ll use DB table Accounts (columns ID, Name, IsActive) with its corresponding Java model class Account:
@Entity
public class Account {
@Column(name="ID")
private int id;
@Column(name="Name")
private String name;
@Column(name="IsActive")
private String isActive;
@Override
public String toString() {
return "ID: " + id + "\n" +
"Name: " + name + "\n"+
"Active: " + isActive + "\n\n";
}
}
Now comes the most interesting part where database ResultSet is mapped to an Object. This is encapsulated in separate class called ResultSetMapper
public class ResultSetMapper<T> {
// This method is already implemented in package
// but as far as I know it accepts only public class attributes
private void setProperty(Object clazz, String fieldName, Object columnValue) {
try {
// get all fields of the class (including public/protected/private)
Field field = clazz.getClass().getDeclaredField(fieldName);
// this is necessary in case the field visibility is set at private
field.setAccessible(true);
field.set(clazz, columnValue);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public List<T> mapRersultSetToObject(ResultSet rs, Class clazz) {
List<T> outputList = null;
try {
// make sure resultset is not null
if (rs != null) {
// check if Class clazz has the 'Entity' annotation
if (clazz.isAnnotationPresent(Entity.class)) {
// get the resultset metadata
ResultSetMetaData rsmd = rs.getMetaData();
// get all the attributes of Class clazz
Field[] fields = clazz.getDeclaredFields();
while (rs.next()) {
T bean = (T) clazz.newInstance();
for (int _iterator = 0; _iterator < rsmd.getColumnCount(); _iterator++) {
// get the SQL column name
String columnName = rsmd.getColumnName(_iterator + 1);
// get the value of the SQL column
Object columnValue = rs.getObject(_iterator + 1);
// iterating over clazz attributes to check
// if any attribute has 'Column' annotation with matching 'name' value
for (Field field : fields) {
if (field.isAnnotationPresent(Column.class)) {
Column column = field.getAnnotation(Column.class);
if (column.name().equalsIgnoreCase(columnName)
&& columnValue != null) {
this.setProperty(bean, field.getName(), columnValue);
break;
}
}
} // EndOf for(Field field : fields)
} // EndOf for(_iterator...)
if (outputList == null) {
outputList = new ArrayList<T>();
}
outputList.add(bean);
} // EndOf while(rs.next())
} else {
// throw some error that Class clazz
// does not have @Entity annotation
}
} else {
// ResultSet is empty
return null;
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return outputList;
}
}
To test if this mapper works if we will connect to database, read some data and print it out:
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
Connection con = null;
Statement st = null;
ResultSet rs = null;
String url = "jdbc:mysql://example.com:3306/finance";
String user = "john_doe";
String password = "P@ssw0rd";
try {
con = DriverManager.getConnection(url, user, password);
st = con.createStatement();
rs = st.executeQuery("select * from accounts");
ResultSetMapper<Account> resultSetMapper = new ResultSetMapper<Account>();
List<Account> accountList = resultSetMapper.mapRersultSetToObject(rs, Account.class);
// print out the list retrieved from database
if(accountList != null){
for(Account account : accountList){
System.out.println(account);
}
} else {
System.out.println("ResultSet is empty. Please check if database table is empty");
}
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Great work! Is it possible for it to work with nested classes?
It is great solution but I think this has negative effect related to memory because you need to store more objects
Perfect….Kudos!!!