Firstname、lastname、dob、username、passwordなどの16の属性を持つユーザークラスがあります。これらはすべてMySQLデータベースに格納され、ユーザーを取得する場合はResultSetを使用します。各列をユーザー属性にマップし直したいのですが、そのやり方は非常に効率が悪いようです。たとえば、私はやっています:
//ResultSet rs;
while(rs.next()) {
String uid = rs.getString("UserId");
String fname = rs.getString("FirstName");
...
...
...
User u = new User(uid,fname,...);
//ArrayList<User> users
users.add(u);
}
つまり、すべての列を取得し、すべての列の値をUserコンストラクターに挿入してユーザーオブジェクトを作成します。
誰もがこれを行うより速く、きれいな方法を知っていますか?
ResultSet値をStringに保存し、再度POJOクラスに設定する必要はありません。代わりに、取得するときに設定します。
または、最良の方法は、POJOオブジェクトをデータベースに直接マップするJDBCではなく、HibernateなどのORMツールに切り替えます。
しかし、今のところこれを使用してください:
List<User> users=new ArrayList<User>();
while(rs.next()) {
User user = new User();
user.setUserId(rs.getString("UserId"));
user.setFName(rs.getString("FirstName"));
...
...
...
users.add(user);
}
OpenJPAやhibernateなどのJPAプロバイダーを使用したくない場合は、Apache dbutilsを試してみてください。
http://commons.Apache.org/proper/commons-dbutils/examples.html
コードは次のようになります
QueryRunner run = new QueryRunner(dataSource);
// Use the BeanListHandler implementation to convert all
// ResultSet rows into a List of Person JavaBeans.
ResultSetHandler<List<Person>> h = new BeanListHandler<Person>(Person.class);
// Execute the SQL statement and return the results in a List of
// Person objects generated by the BeanListHandler.
List<Person> persons = run.query("SELECT * FROM Person", h);
戦略的フレームワークなしでコアJavaを使用すると仮定しましょう。エンティティのフィールド名がデータベースの列と等しいことを保証できる場合は、Reflection APIを使用できます(そうでない場合は、アノテーションを作成し、マッピング名を定義します)。
FieldNameによる
/**
Class<T> clazz - a list of object types you want to be fetched
ResultSet resultSet - pointer to your retrieved results
*/
List<Field> fields = Arrays.asList(clazz.getDeclaredFields());
for(Field field: fields) {
field.setAccessible(true);
}
List<T> list = new ArrayList<>();
while(resultSet.next()) {
T dto = clazz.getConstructor().newInstance();
for(Field field: fields) {
String name = field.getName();
try{
String value = resultSet.getString(name);
field.set(dto, field.getType().getConstructor(String.class).newInstance(value));
} catch (Exception e) {
e.printStackTrace();
}
}
list.add(dto);
}
注釈による
@Retention(RetentionPolicy.RUNTIME)
public @interface Col {
String name();
}
DTO:
class SomeClass {
@Col(name = "column_in_db_name")
private String columnInDbName;
public SomeClass() {}
// ..
}
同じですが、
while(resultSet.next()) {
T dto = clazz.getConstructor().newInstance();
for(Field field: fields) {
Col col = field.getAnnotation(Col.class);
if(col!=null) {
String name = col.name();
try{
String value = resultSet.getString(name);
field.set(dto, field.getType().getConstructor(String.class).newInstance(value));
} catch (Exception e) {
e.printStackTrace();
}
}
}
list.add(dto);
}
思考
実際、すべてのフィールドを反復処理することは効果がないように見える場合があるため、毎回反復処理するのではなく、マッピングをどこかに保存します。ただし、T
がデータ転送のみを目的とするDTOであり、不要なフィールドのロードが含まれない場合は問題ありません。最終的には、定型的な方法をずっと使用するよりもはるかに優れています。
これが誰かを助けることを願っています。
@ TEH-EMPRAHのアイデアと 返すための汎用オブジェクトへのキャストオブジェクト からの汎用キャストを使用した完全なソリューション
import annotations.Column;
import Java.lang.reflect.Field;
import Java.lang.reflect.InvocationTargetException;
import Java.sql.SQLException;
import Java.util.*;
public class ObjectMapper<T> {
private Class clazz;
private Map<String, Field> fields = new HashMap<>();
Map<String, String> errors = new HashMap<>();
public DataMapper(Class clazz) {
this.clazz = clazz;
List<Field> fieldList = Arrays.asList(clazz.getDeclaredFields());
for (Field field : fieldList) {
Column col = field.getAnnotation(Column.class);
if (col != null) {
field.setAccessible(true);
fields.put(col.name(), field);
}
}
}
public T map(Map<String, Object> row) throws SQLException {
try {
T dto = (T) clazz.getConstructor().newInstance();
for (Map.Entry<String, Object> entity : row.entrySet()) {
if (entity.getValue() == null) {
continue; // Don't set DBNULL
}
String column = entity.getKey();
Field field = fields.get(column);
if (field != null) {
field.set(dto, convertInstanceOfObject(entity.getValue()));
}
}
return dto;
} catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
throw new SQLException("Problem with data Mapping. See logs.");
}
}
public List<T> map(List<Map<String, Object>> rows) throws SQLException {
List<T> list = new LinkedList<>();
for (Map<String, Object> row : rows) {
list.add(map(row));
}
return list;
}
private T convertInstanceOfObject(Object o) {
try {
return (T) o;
} catch (ClassCastException e) {
return null;
}
}
}
そして、それがどのようにデータベースと結びついているかという点で、私は次のことを持っています:
// connect to database (autocloses)
try (DataConnection conn = ds1.getConnection()) {
// fetch rows
List<Map<String, Object>> rows = conn.nativeSelect("SELECT * FROM products");
// map rows to class
ObjectMapper<Product> objectMapper = new ObjectMapper<>(Product.class);
List<Product> products = objectMapper.map(rows);
// display the rows
System.out.println(rows);
// display it as products
for (Product prod : products) {
System.out.println(prod);
}
} catch (Exception e) {
e.printStackTrace();
}
import Java.util.ArrayList;
import Java.util.HashMap;
import Java.util.List;
import Java.util.Map;
import org.json.simple.JSONObject;
import com.google.gson.Gson;
public class ObjectMapper {
//generic method to convert JDBC resultSet into respective DTo class
@SuppressWarnings("unchecked")
public static Object mapValue(List<Map<String, Object>> rows,Class<?> className) throws Exception
{
List<Object> response=new ArrayList<>();
Gson gson=new Gson();
for(Map<String, Object> row:rows){
org.json.simple.JSONObject jsonObject = new JSONObject();
jsonObject.putAll(row);
String json=jsonObject.toJSONString();
Object actualObject=gson.fromJson(json, className);
response.add(actualObject);
}
return response;
}
public static void main(String args[]) throws Exception{
List<Map<String, Object>> rows=new ArrayList<Map<String, Object>>();
//Hardcoded data for testing
Map<String, Object> row1=new HashMap<String, Object>();
row1.put("name", "Raja");
row1.put("age", 22);
row1.put("location", "India");
Map<String, Object> row2=new HashMap<String, Object>();
row2.put("name", "Rani");
row2.put("age", 20);
row2.put("location", "India");
rows.add(row1);
rows.add(row2);
@SuppressWarnings("unchecked")
List<Dto> res=(List<Dto>) mapValue(rows, Dto.class);
}
}
public class Dto {
private String name;
private Integer age;
private String location;
//getters and setters
}
上記のコードを試してください。これは、JDBCの結果をそれぞれのDTOクラスにマップするための汎用メソッドとして使用できます。
質問者がMySQLを使用していることは知っていますが、MySQLに制限はないが同じ質問をしている多くの人々がこのページにたどり着くので、q2oをほのめかしたいと思います。 JPAベースのJavaオブジェクトマッパーは、面倒なSQLおよびJDBC ResultSet関連のタスクの多くを支援しますが、ORMフレームワークには複雑さはありません。ResultSetをオブジェクトはこれと同じくらい簡単です:
while(rs.next()) {
users.add(Q2Obj.fromResultSet(rs, User.class));
}