package jef.database.wrapper.populator;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import jef.accelerator.bean.BeanAccessor;
import jef.accelerator.bean.FastBeanWrapperImpl;
import jef.common.log.LogUtil;
import jef.database.Field;
import jef.database.IQueryableEntity;
import jef.database.dialect.type.ColumnMapping;
import jef.database.dialect.type.ColumnMappings;
import jef.database.dialect.type.ResultSetAccessor;
import jef.database.innerpool.ArrayElementPopulator;
import jef.database.innerpool.NestedObjectPopulator;
import jef.database.jdbc.result.IResultSet;
import jef.database.meta.AliasProvider;
import jef.database.meta.ITableMetadata;
import jef.database.meta.MetaHolder;
import jef.tools.Assert;
import jef.tools.StringUtils;
import jef.tools.reflect.BeanWrapper;
/**
* Mapper工具类,用于生成一些默认的Mapper对象。
* 用户可以提供一个Class,生成这个class和数据库列的映射关系。
* 个别字段不一致的,用户可以微调。
*
*
* <p>
* 提供了这三类的自动映射关系的生成器
* <p>
* <li>{@link #toResultBean} 将字段映射后直接赋值到对象里</li> <li>{@link #toResultProperty}
* 将映射后的对象放入返回对象的某个字段里(比如父子关系的表)</li> <li>{@link #toArrayElement}
* 将映射后的对象放入数组中(一次返回多个对象时使用数组)</li>
*
*
* @author jiyi
* @see BeanMapper
*/
public final class Mappers {
private Mappers() {
}
/**
* 转换器:将ResultSet中名称和clz的列一致的数据填入对象中,该对象是最终结果的成员变量。<br />
*
* @param fieldName
* The fieldname where the transformed bean will set into.
* @param clz
* the type of transformed bean
* @param schema
* the prefix of the alias in SQL 'select ' items. such as in
* 'select id as T1__ID ...', here 'T1' is the schema. we use
* "__" as a divider between schema and column names.
* <p>
* @return The Mapper
*
* @Example <pre>
*
* <code> public class Student{ private Teacher teacher;
*
* ... getters and setters ... }
*
* //query SQL NativeQuery<Student>
* query=db.createNativeQuery("select s.*," +
* "teacher_id as T2__TEACHER_ID," + "teacher_name as T2__TEACHER"
* + "from Student s, Teacher t2 where s.teacher_id=t2.thacher_id",
* Student.class);
*
* //use 'ResultTransformer' and 'Mappings' to transform tacher
* info into student..
* //NOTE: teacher's info will transform to the field 'teacher'
* into Student.
* query.getResultTransformer().addMapper(Mappers.toResultProperty(
* "teacher", Teacher.class, "T2")); </code>
*
* </pre>
*/
public static final <T extends IQueryableEntity> BeanMapper<T> toResultProperty(String fieldName, Class<T> clz, String schema) {
ITableMetadata meta = MetaHolder.getMeta(clz);
return toResultProperty(fieldName, meta, schema);
}
/**
* 转换器:将ResultSet中名称和clz的列一致的数据填入对象中,该对象是最终结果的成员变量。<br />
*
* @param fieldName
* The fieldname where the transformed bean will set into.
* @param meta
* the table metadata of transformed bean
* @param schema
* the prefix of the alias in SQL 'select ' items. such as in
* 'select id as T1__ID ...', here 'T1' is the schema. we use
* "__" as a divider between the schema and column name.
* <p>
* @return The Mapper
*
* @Example <pre>
*
* <code> public class Student{ private Teacher teacher;
*
* ... getters and setters ... }
*
* //query SQL NativeQuery<Student>
* query=db.createNativeQuery("select s.*," +
* "teacher_id as T2__TEACHER_ID," + "teacher_name as T2__TEACHER"
* + "from Student s, Teacher t2 where s.teacher_id=t2.thacher_id",
* Student.class);
*
* //use 'ResultTransformer' and 'Mappings' to transform tacher
* info into student..
* //NOTE: teacher's info will transform to the field 'teacher'
* into Student. ITableMetadata meta=MetaHolder.get(Teacher.class);
* query.getResultTransformer().addMapper(Mappers.toResultProperty(
* "teacher", meta, "T2")); </code>
*
* </pre>
*/
public static final <T extends IQueryableEntity> BeanMapper<T> toResultProperty(String fieldName, ITableMetadata meta, String schema) {
BeanMapper<T> result = new BeanMapper<T>(meta);
result.schema = schema;
result.toField = fieldName;
return result;
}
/**
* 转换器:将ResultSet中名称和clz的列一致的数据填入对象中,该对象是最终结果的成员变量。<br />
* <h3>Eg.</h3>
*
* <pre>
* <code>// The Object Model
* public class Student{
* private Teacher teacher; //Note: there is a field named 'teacher' in class Student.
*
* ... getters and setters ...
* }
*
* //query SQL —— Note: all column in result set are mapping to class student except column 'teacher_id' and 'teacher_name'.
* NativeQuery<Student> query=db.createNativeQuery("select s.*,t.teacher_id,t.teacher_name from Student s, Teacher t where s.teacher_id=t.thacher_id", Student.class);
*
*
* //use 'ResultTransformer' and 'Mappings' to transform tacher info into student..
* //The ORM will fetch 'teacher_id' and 'teacher_name' column into a Teacher Object.
* //NOTE: There is a field named 'teacher' in class 'Student', the teacher's info will be set to this field.
* query.getResultTransformer().addMapper(Mappers.toResultProperty("teacher", Teacher.class));
* </code>
* </pre>
* <h3>使用举例(中文版)</h3>
* <pre>
* <code>// 对象模型
* public class Student{
* private Teacher teacher; //Note: there is a field named 'teacher' in class Student.
*
* ... getters and setters ...
* }
*
* //查询 SQL —— Note: all column in result set are mapping to class student except column 'teacher_id' and 'teacher_name'.
* NativeQuery<Student> query=db.createNativeQuery("select s.*,t.teacher_id,t.teacher_name from Student s, Teacher t where s.teacher_id=t.thacher_id", Student.class);
*
*
* //use 'ResultTransformer' and 'Mappings' to transform tacher info into student..
* //The ORM will fetch 'teacher_id' and 'teacher_name' column into a Teacher Object.
* //NOTE: There is a field named 'teacher' in class 'Student', the teacher's info will be set to this field.
* query.getResultTransformer().addMapper(Mappers.toResultProperty("teacher", Teacher.class));
* </code>
* </pre>
*
* Above is the usega of #toResultProperty
*
* @param fieldName
* @param clz
* @return Mapper对象
*/
public static final <T extends IQueryableEntity> BeanMapper<T> toResultProperty(String fieldName, Class<T> clz) {
ITableMetadata meta = MetaHolder.getMeta(clz);
return toResultProperty(fieldName, meta, null);
}
/**
* 转换器:将ResultSet中名称和clz的列一致的数据填入对象中<br />
*
* @param clz
* @return Mapper对象
*/
public static final <T extends IQueryableEntity> BeanMapper<T> toResultBean(Class<T> clz) {
return new BeanMapper<T>(MetaHolder.getMeta(clz));
}
/**
* 转换器:将ResultSet中名称和clz的列一致的数据填入对象中<br />
*
* @param clz
* @param schema
* @return Mapper对象
*/
public static final <T extends IQueryableEntity> BeanMapper<T> toResultBean(Class<T> clz, String schema) {
BeanMapper<T> result = new BeanMapper<T>(MetaHolder.getMeta(clz));
result.schema = schema;
return result;
}
/**
* 转换器:将ResultSet中名称和clz的列一致的数据填入对象中<br />
*
* @param meta
* @param schema
* @return Mapper对象
*/
public static final <T extends IQueryableEntity> BeanMapper<T> toResultBean(ITableMetadata meta, String schema) {
BeanMapper<T> result = new BeanMapper<T>(meta);
result.schema = schema;
return result;
}
/**
* 转换器:将ResultSet中名称和clz的列一致的数据拼装为Object后填入数组中<br />
* <h3>使用场景</h3> 使用自定义的查询查出多个对象,并将这些对象用数组的形式成对返回。
*
* <h3>用法</h3> ...
*
* @param index
* 数组中的序号,从0开始
* @param clz
* 数据类型
* @return Mapper对象
*/
public static final <T extends IQueryableEntity> BeanMapper<T> toArrayElement(int index, Class<T> clz) {
return toResultProperty(String.valueOf(index), clz, null);
}
/**
* 转换器:将ResultSet中名称和clz的列一致的数据拼装为Object后填入数组中<br />
*
* @param index
* 对象数组的序号,从0开始
* @param clz
* 对象类型
* @param schema
* 对象所在的表alias
* @return Mapper对象
*/
public static final <T extends IQueryableEntity> BeanMapper<T> toArrayElement(int index, Class<T> clz, String schema) {
return toResultProperty(String.valueOf(index), clz, schema);
}
/**
* 转换器:将ResultSet中名称和clz的列一致的数据拼装为Object后填入数组中<br />
* <h3>使用场景</h3> 使用自定义的查询查出多个对象,并将这些对象用数组的形式成对返回。
*
* <h3>用法</h3> ...
*
* @param index
* 对象数组的序号,从0开始
* @param meta
* 对象类型
* @param schema
* 对象所在的表alias
* @return Mapper对象
*/
public static final <T extends IQueryableEntity> BeanMapper<T> toArrayElement(int index, ITableMetadata meta, String schema) {
return toResultProperty(String.valueOf(index), meta, schema);
}
/**
* 将一个java数据模型和数据库中的列自动映射起来。然后允许用户通过{@link #adjust(String, String)}方法微调映射关系。
* @author jiyi
*
* @param <T>
*/
public final static class BeanMapper<T extends IQueryableEntity> extends Mapper<T> {
String toField;
private ITableMetadata meta;
private String schema;
private boolean skipColumnAnnotation;
private IPopulator populator;
@SuppressWarnings("unchecked")
private Map<String,String> customMap=Collections.EMPTY_MAP; //指定对象属性 -> 数据库列定制 (字段属性全大写。数据库列全大写)
/**
* 映射关系微调
* @param field 在java中的属性名
* @param column 在数据库中的列名
* @return BeanMapper本身
*/
public BeanMapper<T> adjust(String field,String column){
Assert.notNull(field);
Assert.notNull(column);
if(customMap==Collections.EMPTY_MAP){
customMap=new HashMap<String,String>();
}
customMap.put(field.toUpperCase(), column.toUpperCase());
return this;
}
public BeanMapper(ITableMetadata meta) {
this.meta = meta;
}
@Override
public void process(BeanWrapper wrapper, IResultSet rs) throws SQLException {
populator.process(wrapper, rs);
}
@Override
protected void prepare(Map<String, ColumnDescription> nameIndex) {
HashMap<String, ColumnDescription> data = new HashMap<String, ColumnDescription>();
if (meta == null) {
throw new IllegalArgumentException("the table metadata is null!");
}
BeanAccessor ba = FastBeanWrapperImpl.getAccessorFor(meta.getThisType());
for (ColumnMapping ft : meta.getColumns()) {
Field f = ft.field();
String columnName=customMap.get(f.name().toUpperCase());
if(columnName==null){
if (schema == null) {
columnName = skipColumnAnnotation ? f.name().toUpperCase() : ft.upperColumnName();
} else {
columnName = AliasProvider.DEFAULT.getResultAliasOf(ft, schema);
}
}
if (columnName != null) {
ColumnDescription columnDesc = nameIndex.get(columnName);
if (columnDesc == null) {
if (schema == "")
LogUtil.warn("Warnning: populating object " + meta.getThisType() + " error," + schema + ":" + columnName + " not found in the selected columns");
} else {
ResultSetAccessor accessor = ColumnMappings.getAccessor(ba.getPropertyType(f.name()), ft, columnDesc, false);
columnDesc.setAccessor(accessor);
data.put(f.name(), columnDesc);
}
}
}
if (StringUtils.isNotEmpty(toField)) {
if (StringUtils.isNumeric(toField)) {
int index = StringUtils.toInt(toField, null);
this.populator = new ArrayElementPopulator(index, new ObjectPopulator(meta, data));
} else {
this.populator = new NestedObjectPopulator(toField, new ObjectPopulator(meta, data));
}
} else {
this.populator = new ObjectPopulator(meta, data);
}
}
protected void transform(T obj, IResultSet rs) throws SQLException {
throw new UnsupportedOperationException();
}
}
}