package org.nutz.dao.impl.entity;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.sql.DataSource;
import org.nutz.dao.DB;
import org.nutz.dao.entity.Entity;
import org.nutz.dao.entity.EntityField;
import org.nutz.dao.entity.EntityMaker;
import org.nutz.dao.entity.MappingField;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.EL;
import org.nutz.dao.entity.annotation.Id;
import org.nutz.dao.entity.annotation.Index;
import org.nutz.dao.entity.annotation.Many;
import org.nutz.dao.entity.annotation.ManyMany;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.One;
import org.nutz.dao.entity.annotation.PK;
import org.nutz.dao.entity.annotation.SQL;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableIndexes;
import org.nutz.dao.entity.annotation.TableMeta;
import org.nutz.dao.entity.annotation.View;
import org.nutz.dao.impl.EntityHolder;
import org.nutz.dao.impl.entity.field.ManyLinkField;
import org.nutz.dao.impl.entity.field.ManyManyLinkField;
import org.nutz.dao.impl.entity.field.NutMappingField;
import org.nutz.dao.impl.entity.field.OneLinkField;
import org.nutz.dao.impl.entity.info.LinkInfo;
import org.nutz.dao.impl.entity.info.MappingInfo;
import org.nutz.dao.impl.entity.info.TableInfo;
import org.nutz.dao.impl.entity.info._Infos;
import org.nutz.dao.impl.entity.macro.ElFieldMacro;
import org.nutz.dao.impl.entity.macro.SqlFieldMacro;
import org.nutz.dao.jdbc.JdbcExpert;
import org.nutz.dao.jdbc.Jdbcs;
import org.nutz.dao.sql.Pojo;
import org.nutz.lang.Lang;
import org.nutz.lang.Mirror;
import org.nutz.lang.Strings;
import org.nutz.lang.segment.CharSegment;
import org.nutz.log.Log;
import org.nutz.log.Logs;
/**
* 根据一个 Class 对象生成 Entity 的实例
*
* @author zozoh(zozohtnt@gmail.com)
*/
public class AnnotationEntityMaker implements EntityMaker {
private static final Log log = Logs.get();
private DataSource datasource;
private JdbcExpert expert;
private EntityHolder holder;
public AnnotationEntityMaker(DataSource datasource, JdbcExpert expert, EntityHolder holder) {
this.datasource = datasource;
this.expert = expert;
this.holder = holder;
}
public <T> Entity<T> make(Class<T> type) {
NutEntity<T> en = new NutEntity<T>(type);
TableInfo ti = _createTableInfo(type);
/*
* 获取实体的扩展描述
*/
// 全局
if (null != expert.getConf()) {
for (String key : expert.getConf().keySet())
en.getMetas().put(key, expert.getConf().get(key));
}
// 当前表
if (null != ti.annMeta) {
Map<String, Object> map = Lang.map(ti.annMeta.value());
for (Entry<String, Object> entry : map.entrySet()) {
en.getMetas().put(entry.getKey(), entry.getValue().toString());
}
}
/*
* 获得表名以及视图名称
*/
String tableName = null == ti.annTable ? Strings.lowerWord(type.getSimpleName(), '_')
: ti.annTable.value();
String viewName = null == ti.annView ? tableName : ti.annView.value();
en.setTableName(tableName);
en.setViewName(viewName);
/*
* 获取所有的数据库字段
*/
// 字段里面是不是有声明过 '@Column'
boolean shouldUseColumn = false;
for (Field field : en.getMirror().getFields()) {
if (null != field.getAnnotation(Column.class)) {
shouldUseColumn = true;
break;
}
}
/*
* 循环获取实体字段
*/
List<MappingInfo> infos = new ArrayList<MappingInfo>();
List<LinkInfo> ones = new ArrayList<LinkInfo>();
List<LinkInfo> manys = new ArrayList<LinkInfo>();
List<LinkInfo> manymanys = new ArrayList<LinkInfo>();
// 循环所有的字段,查找有没有数据库映射字段
for (Field field : en.getMirror().getFields()) {
// '@One'
if (null != field.getAnnotation(One.class)) {
ones.add(_Infos.createLinkInfo(field));
}
// '@Many'
else if (null != field.getAnnotation(Many.class)) {
manys.add(_Infos.createLinkInfo(field));
}
// '@ManyMany'
else if (null != field.getAnnotation(ManyMany.class)) {
manymanys.add(_Infos.createLinkInfo(field));
}
// 应该忽略
else if (shouldUseColumn
&& (null == field.getAnnotation(Column.class)
&& null == field.getAnnotation(Id.class) && null == field.getAnnotation(Name.class))) {
continue;
}
// '@Column'
else {
infos.add(_Infos.createMappingInfo(ti.annPK, field));
}
}
// 循环所有方法,查找有没有虚拟数据库映射字段
for (Method method : en.getType().getMethods()) {
// '@One'
if (null != method.getAnnotation(One.class)) {
ones.add(_Infos.createLinkInfo(method));
}
// '@Many'
else if (null != method.getAnnotation(Many.class)) {
manys.add(_Infos.createLinkInfo(method));
}
// '@ManyMany'
else if (null != method.getAnnotation(ManyMany.class)) {
manymanys.add(_Infos.createLinkInfo(method));
}
// 应该忽略
else if (null == method.getAnnotation(Column.class)
&& null == method.getAnnotation(Id.class)
&& null == method.getAnnotation(Name.class)) {
continue;
}
// '@Column'
else {
infos.add(_Infos.createMapingInfo(ti.annPK, method));
}
}
/*
* 解析所有映射字段
*/
for (MappingInfo info : infos) {
NutMappingField ef = new NutMappingField(en);
_evalMappingField(ef, info);
en.addMappingField(ef);
}
holder.set(en); // 保存一下,这样别的实体映射到这里时会用的到
/*
* 解析所有关联字段
*/
// 一对一 '@One'
for (LinkInfo li : ones) {
en.addLinkField(new OneLinkField(en, holder, li));
}
// 一对多 '@Many'
for (LinkInfo li : manys) {
en.addLinkField(new ManyLinkField(en, holder, li));
}
// 多对多 '@ManyMany'
for (LinkInfo li : manymanys) {
en.addLinkField(new ManyManyLinkField(en, holder, li));
}
// 检查复合主键
en.checkCompositeFields(null == ti.annPK ? null : ti.annPK.value());
/*
* 交付给 expert 来检查一下数据库一致性
*/
if (null != datasource && null != expert) {
_checkupEntityFieldsWithDatabase(en);
}
/*
* 检查字段宏
*/
_evalFieldMacro(en, infos);
/*
* 解析实体索引
*/
if (null != ti.annIndexes)
_evalEntityIndexes(en, ti.annIndexes);
// 搞定收工,哦耶 ^_^
return en;
}
/**
* 向父类递归查找实体的配置
*
* @param type
* 实体类型
* @return 实体表描述
*/
private TableInfo _createTableInfo(Class<?> type) {
TableInfo info = new TableInfo();
Mirror<?> mirror = Mirror.me(type);
info.annTable = mirror.getAnnotation(Table.class);
info.annView = mirror.getAnnotation(View.class);
info.annMeta = mirror.getAnnotation(TableMeta.class);
info.annPK = mirror.getAnnotation(PK.class);
info.annIndexes = mirror.getAnnotation(TableIndexes.class);
return info;
}
/**
* 根据 '@Next' 和 '@Prev' 的信息,生成一个 FieldMacroInfo 对象
*
* @param els
* 表达式
* @param sqls
* SQL
* @return 一个字段宏信息的列表
*/
private List<FieldMacroInfo> _annToFieldMacroInfo(EL[] els, SQL[] sqls) {
List<FieldMacroInfo> mis = new LinkedList<FieldMacroInfo>();
if (els.length > 0) { // els 没有机会为 null 的
for (EL el : els)
mis.add(new FieldMacroInfo(el));
}
if (sqls.length > 0) { // @SQL 没有 @EL 优先级高
for (SQL sql : sqls)
mis.add(new FieldMacroInfo(sql));
}
return mis;
}
/**
* @param ef
* @param info
*/
private void _evalMappingField(NutMappingField ef, MappingInfo info) {
// 字段的 Java 名称
ef.setName(info.name);
ef.setType(info.fieldType);
// 字段的数据库名
if (null == info.annColumn || Strings.isBlank(info.annColumn.value()))
ef.setColumnName(info.name);
else
ef.setColumnName(info.annColumn.value());
// Id 字段
if (null != info.annId) {
ef.setAsId();
if (info.annId.auto())
ef.setAsAutoIncreasement();
}
// Name 字段
if (null != info.annName) {
ef.setAsName();
ef.setCasesensitive(info.annName.casesensitive());
}
// 检查 @Id 和 @Name 的冲突
if (ef.isId() && ef.isName())
throw Lang.makeThrow( "Field '%s'(%s) can not be @Id and @Name at same time!",
ef.getName(),
ef.getEntity().getType().getName());
// 检查 PK
if (null != info.annPK) {
// 用 @PK 的方式声明的主键
if (info.annPK.value().length == 1) {
if (Lang.contains(info.annPK.value(), info.name)) {
if (ef.getTypeMirror().isIntLike())
ef.setAsId();
else
ef.setAsName();
}
}
// 看看是不是复合主键
else if (Lang.contains(info.annPK.value(), info.name))
ef.setAsCompositePk();
}
// 默认值
if (null != info.annDefault)
ef.setDefaultValue(new CharSegment(info.annDefault.value()));
// 只读
if (null != info.annReadonly)
ef.setAsReadonly();
// 字段更多定义
if (null != info.annDefine) {
// 类型
ef.setColumnType(info.annDefine.type());
// 宽度
ef.setWidth(info.annDefine.width());
// 精度
ef.setPrecision(info.annDefine.precision());
// 无符号
if (info.annDefine.unsigned())
ef.setAsUnsigned();
// 非空约束
if (info.annDefine.notNull())
ef.setAsNotNull();
// 自增,如果 @Id(auto=false),则忽略
if (info.annDefine.auto() && !ef.isId())
ef.setAsAutoIncreasement();
}
// 猜测字段类型
else {
Jdbcs.guessEntityFieldColumnType(ef);
}
// 字段值的适配器
ef.setAdaptor(expert.getAdaptor(ef));
// 输入输出
ef.setInjecting(info.injecting);
ef.setEjecting(info.ejecting);
}
private void _evalFieldMacro(Entity<?> en, List<MappingInfo> infos) {
for (MappingInfo info : infos) {
// '@Prev' : 预设值
if (null != info.annPrev) {
en.addBeforeInsertMacro(__macro(en.getField(info.name),
_annToFieldMacroInfo( info.annPrev.els(),
info.annPrev.value())));
}
// '@Next' : 后续获取
if (null != info.annNext
&& en.addAfterInsertMacro(__macro( en.getField(info.name),
_annToFieldMacroInfo( info.annNext.els(),
info.annNext.value())))) {
continue;
}
// '@Id' : 的自动后续获取
else if (null != info.annId && info.annId.auto()) {
MappingField idField = en.getField(info.name);
String autoSql = "SELECT MAX($field) AS $field FROM $view";
Pojo autoInfo = new SqlFieldMacro(idField, autoSql);
autoInfo.setEntity(en);
en.addAfterInsertMacro(autoInfo);
}
}
}
private Pojo __macro(MappingField ef, List<FieldMacroInfo> infoList) {
FieldMacroInfo theInfo = null;
// 根据当前数据库,找到合适的宏
for (FieldMacroInfo info : infoList) {
if (DB.OTHER == info.getDb()) {
theInfo = info;
} else if (info.getDb().name().equalsIgnoreCase(expert.getDatabaseType())) {
theInfo = info;
break;
}
}
// 如果找到,增加
if (null != theInfo) {
if (theInfo.isEl())
return new ElFieldMacro(ef, theInfo.getValue());
else
return new SqlFieldMacro(ef, theInfo.getValue());
}
return null;
}
private void _evalEntityIndexes(NutEntity<?> en, TableIndexes indexes) {
for (Index idx : indexes.value()) {
NutEntityIndex index = new NutEntityIndex();
index.setUnique(idx.unique());
index.setName(idx.name());
for (String indexName : idx.fields()) {
EntityField ef = en.getField(indexName);
if (null == ef) {
throw Lang.makeThrow( "Fail to find field '%s' in '%s' by @Index(%s:%s)",
indexName,
en.getType().getName(),
index.getName(),
Lang.concat(idx.fields()));
}
index.addField(ef);
}
en.addIndex(index);
}
}
private void _checkupEntityFieldsWithDatabase(NutEntity<?> en) {
Connection conn = null;
try {
conn = datasource.getConnection();
expert.setupEntityField(conn, en);
}
catch (Exception e) {
if (log.isDebugEnabled())
log.debugf( "Fail to setup '%s'(%s) by DB, because: (%s)'%s'",
en.getType().getName(),
en.getTableName(),
e.getClass().getName(),
e.getMessage());
}
finally {
if (null != conn) {
try {
conn.close();
}
catch (SQLException e) {
if (log.isWarnEnabled())
log.warn("Fail to close connection!", e);
}
}
}
}
}