All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tsc9526.monalisa.orm.model.ModelMeta Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
/*******************************************************************************************
 *	Copyright (c) 2016, zzg.zhou([email protected])
 * 
 *  Monalisa is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU Lesser General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.

 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU Lesser General Public License for more details.

 *	You should have received a copy of the GNU Lesser General Public License
 *	along with this program.  If not, see .
 *******************************************************************************************/
package com.tsc9526.monalisa.orm.model;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

import com.tsc9526.monalisa.orm.annotation.Column;
import com.tsc9526.monalisa.orm.annotation.DB;
import com.tsc9526.monalisa.orm.annotation.Index;
import com.tsc9526.monalisa.orm.annotation.Table;
import com.tsc9526.monalisa.orm.datasource.DBConfig;
import com.tsc9526.monalisa.orm.datasource.DbProp;
import com.tsc9526.monalisa.orm.dialect.Dialect;
import com.tsc9526.monalisa.orm.meta.MetaColumn;
import com.tsc9526.monalisa.orm.meta.MetaPartition;
import com.tsc9526.monalisa.orm.meta.MetaTable;
import com.tsc9526.monalisa.orm.utils.TableHelper;
import com.tsc9526.monalisa.tools.Tasks;
import com.tsc9526.monalisa.tools.clazz.MelpClass;
import com.tsc9526.monalisa.tools.clazz.MelpClass.ClassHelper;
import com.tsc9526.monalisa.tools.clazz.MelpClass.FGS;
import com.tsc9526.monalisa.tools.logger.Logger;
import com.tsc9526.monalisa.tools.string.MelpString;
import com.tsc9526.monalisa.tools.validator.Validator;
 
/**
 * 
 * @author zzg.zhou([email protected])
 */
public class ModelMeta{	
	static Logger logger=Logger.getLogger(ModelMeta.class.getName());
	
	private static boolean modelReloadRunning=false;
	private static Map hMonitorMetas=new ConcurrentHashMap();
	private static Map hMetas       =new ConcurrentHashMap();
	
	public static ModelMeta getModelMeta(Model model){
		synchronized(model){
			String key=getModelKey(model);
			
			ModelMeta mm=hMetas.get(key);
		 	if(mm==null || mm.iChanged()){
				mm=createModelMeta(model,key);
			}
		 	
	 	 	return mm;
		}	
	}
	
	private static ModelMeta createModelMeta(Model model,String key){
		ModelMeta mm=new ModelMeta(key);
		mm.init(model);
		
		if(mm.record){
			if(!hMonitorMetas.containsKey(key)){
				logger.info("Add dynamic table: "+mm.tableName+", dbkey: "+mm.db.getKey());
			}
			hMonitorMetas.put(key, mm);
			
			if(!modelReloadRunning){
				modelReloadRunning=true;
				startReloadModelMetas();
			}
		}
		hMetas.put(key, mm);
		return mm;
	}
	
	private static void startReloadModelMetas(){
		int interval=DbProp.CFG_RELOAD_MODEL_INTERVAL;
		if(interval>0){
			Tasks.instance.addSchedule("ModelChangeTask", new TimerTask() {
				public void run() {
					reloadModelMetas();
				}
			}, interval*1000, interval*1000);
		}
	}
	 
	public synchronized static void reloadModelMetas(){
		//check if data source changed
		for(ModelMeta mm:hMonitorMetas.values()){
			mm.db.getDataSource();
		}
		
		for(ModelMeta mm:hMonitorMetas.values()){
			mm.checkChanged();
		} 
	}
	
	public synchronized static void clearReloadModelMetas(String dbKey){
		List rms=new ArrayList();
		for(String key:hMonitorMetas.keySet()){
			ModelMeta mm=hMonitorMetas.get(key);
			if(dbKey.equals( mm.db.getKey() )){
				rms.add(key);
			} 
		}
		
		if(rms.size()>0){
			logger.info("Remove dynamic tables: "+rms.toString()+", dbkey: "+dbKey);
			for(String key:rms){
				hMonitorMetas.remove(key);
				hMetas.remove(key);
			}
		}
	}
	
	private static String getModelKey(Model model){
		String key=model.getClass().getName();
		
		Class clazz=MelpClass.findClassWithAnnotation(model.getClass(),DB.class);
		if(clazz==null){
			if(model.$db==null){
				throw new RuntimeException("Dynamic model can not found DB, call model.use(DB) first!");
			}
			
			Table table=model.getClass().getAnnotation(Table.class);
			if(table==null){
				if(model.$tableName==null || model.$tableName.trim().length()<1){
					throw new RuntimeException("Dynamic model can not found table: "+model.$tableName+", call model(TableName) first!");
				}else{				
					key="#"+model.$db.getKey()+"$"+model.$tableName;
				}
			}
		}else{
			Table table=model.getClass().getAnnotation(Table.class);
			if(table==null){
				if(model.$tableName==null || model.$tableName.trim().length()<1){
					throw new RuntimeException("Dynamic model can not found table: "+model.$tableName+", call model(TableName) first!");
				}else{	
					DBConfig db=Model.dsm.getDBConfig(clazz);
					key=db.getKey()+"$"+model.$tableName;
				}
			}
		}
	 
		return key;
	}
	
	protected DBConfig  db;
	protected Dialect   dialect;
	
	protected String      tableName   = null;
	protected String[]    primaryKeys = null;
	protected Validator   validator   = null;
	 
	protected FGS       autoField;	
	protected Table     table;
	
	protected MetaPartition    mp;		 
	protected ModelListener    listener;
	protected List indexes=new ArrayList();
	
	protected Map hFieldsByColumnName=new LinkedHashMap();
	protected Map hFieldsByJavaName  =new LinkedHashMap();
	
	protected boolean record=false;
	protected boolean changed=false;
	
	protected String key;
	private ModelMeta(String key){		
		this.key=key;
	}
	
	void init(Model model){		
		this.tableName  =model.$tableName;
		this.primaryKeys=model.$primaryKeys;
		
		initDB(model);
		
		initTable(model);
				
		initFields(model);
		
		initIndexes(model);
		
		initListeners(model);
		
		initPartioners(model); 
	}
	
	protected void initDB(Model model) {
		Class clazz=MelpClass.findClassWithAnnotation(model.getClass(),DB.class);
		if(clazz!=null){
			db=Model.dsm.getDBConfig(clazz);
		}else{
			db=model.$db;
		}
		
		if(db==null){
			throw new RuntimeException("Model: "+model.getClass()+" must implement interface annotated by: "+DB.class+", Or call use(db) first!");
		}
		
		dialect=Model.dsm.getDialect(db);
	}
	
	protected void initTable(Model model) {
		table=model.getClass().getAnnotation(Table.class);			  
		if(table==null){
			if(tableName==null || tableName.trim().length()==0){
				tableName=model.getClass().getSimpleName();
			}
			
			table=createTable(tableName,primaryKeys);
		}else{
			tableName=table.name();
			primaryKeys=table.primaryKeys();
		}
	}
	
	protected void initFields(Model model){
		List fields=loadModelFields(model);		
		
		List pks=new ArrayList();
		for(Object o:fields){
			FGS fgs=(FGS)o;				
			Column c=fgs.getAnnotation(Column.class);
			
			hFieldsByColumnName.put(c.name().toLowerCase(),fgs);
			hFieldsByJavaName  .put(fgs.getFieldName().toLowerCase(),fgs);
			
			if(c.auto() && autoField==null){
				autoField=fgs;					 
			}
			
			if(c.key()){
				pks.add(c.name());
			}
		}
		
		if(primaryKeys==null || primaryKeys.length==0){
			primaryKeys=pks.toArray(new String[0]);
		}				
	}
	
	protected void initIndexes(Model model) {
		Index[] tbIndexes=table.indexes();
		if(tbIndexes!=null && tbIndexes.length>0){
			for(Index index:tbIndexes){
				ModelIndex mIndex=new ModelIndex();
				mIndex.setName(index.name());
				mIndex.setType(index.type());
				mIndex.setUnique(index.unique());
				
				List fs=new ArrayList();
				for(String f:index.fields()){
					FGS x=findFieldByName(f);
					
					assert x!=null;
					
					fs.add(x);
				}
				mIndex.setFields(fs);
				
				indexes.add(mIndex);
			}
		}
	}
	
	protected void initListeners(Model model){
		String ls=DbProp.PROP_TABLE_MODEL_LISTENER.getValue(db,tableName);
		
		if(ls==null){
			ls=db.getCfg().getModelListener();
		}
		
		if(ls!=null && ls.trim().length()>0){
			try{
				listener=(ModelListener)MelpClass.forName(ls.trim()).newInstance();
			}catch(Exception e){
				throw new RuntimeException("Invalid model listener class: "+ls.trim()+", "+e,e);
			}
		}		
	}
	
	protected void initPartioners(Model model){
		mp=db.getCfg().getPartition(table.name());
	}
	
	protected List loadModelFields(Model model){
		ClassHelper metaClass=MelpClass.getClassHelper(model.getClass());
		List fields=metaClass.getFieldsWithAnnotation(Column.class);						
		if(fields.size()==0){
			record=true;
			fields=loadFieldsFromDB(metaClass);	
			
			StringBuilder sb=new StringBuilder();
			for(FGS f:fields){
				if(sb.length()>0){
					sb.append(", ");
				}
				sb.append(f.getFieldName());
			}
			logger.info("Load table: "+tableName+"{"+sb.toString()+"}");
		}
		
		return fields;		
	}
	
	public void checkChanged(){
		if(mTable!=null && changed==false){
			try{
				MetaTable t2=TableHelper.getMetaTable(db, tableName);
				if(isTableFieldChanged(this.mTable,t2)){
					logger.info("Table struct changed: "+tableName);
					this.changed=true;
				}
			}catch(Exception e){
				logger.error("Check table: "+tableName+" exception: "+e,e);
			}
		}
	}
	
	private boolean isTableFieldChanged(MetaTable t1,MetaTable t2){
		if(t1==null || t2==null){
			return false;
		}
		
		for(MetaColumn x:t1.getColumns()){
			MetaColumn y=t2.getColumn(x.getName());
			if(y!=null){
				if(x.getJdbcType()!=y.getJdbcType() || x.getLength() !=y.getLength()){
					return true;
				}
			}else{
				return true;
			}
		}
		
		for(MetaColumn x:t2.getColumns()){
			MetaColumn y=t1.getColumn(x.getName());
			if(y!=null){
				if(x.getJdbcType()!=y.getJdbcType() || x.getLength() !=y.getLength()){
					return true;
				}
			}else{
				return true;
			}
		}
		
		return false;
	}
	
	
	private MetaTable mTable;
	protected List loadFieldsFromDB(ClassHelper metaClass) {
		try{
			mTable=TableHelper.getMetaTable(db, tableName);
			if(mTable!=null){				 
				List fs=new ArrayList();
				 
				for(MetaColumn c:mTable.getColumns()){
					FGS mfd=metaClass.getField(c.getJavaName());
					if(mfd==null){
						mfd=metaClass.getField(c.getName());
					}
					 
					FGS fgs=createFGS(c,mfd);	
					fs.add(fgs);								 
				}		
				 
				return fs;
			}else{
				throw new RuntimeException("Table not found: "+tableName+", DB: "+db.getKey());
			}			
		}catch(SQLException e){
			throw new RuntimeException(e);
		}
	}
	 
	public FGS findFieldByName(String name){
		name=name.toLowerCase();
		
		name=Dialect.getRealname(name);
		
		FGS fgs=hFieldsByColumnName.get(name);
		if(fgs==null){
			fgs=hFieldsByJavaName.get(name);
		}
		return fgs;
	}
 
	
	public Collection fields(){
		return hFieldsByColumnName.values();
	}
		 
	public List getPkFields(){
		List pks=new ArrayList();
		 
		for(Object o:fields()){
			FGS fgs=(FGS)o;
			Column c=fgs.getAnnotation(Column.class);
			if(c.key()){
				pks.add(fgs);
			}
		}
		return pks;
	}
	
	/**
	 * 复制对象数据
	 * @param model model to copy
	 * @return copy of the model
	 */
	public Model copyModel(Model model){
		try{
			Model x=model.shallow();
			
			ModelHolder f=model.holder();
			ModelHolder t=x.holder();
			 
			t.updateKey  = model.holder().updateKey;			
			t.readonly   = model.holder().readonly;
			t.dirty      = model.holder().dirty;
			t.entity     = model.holder().entity;
			  
			t.fieldFilterExcludeMode=model.holder().fieldFilterExcludeMode;
			t.fieldFilterSets.addAll(model.holder().fieldFilterSets);
			if(f.hModelValues!=null){
				t.getModelValues().putAll(f.hModelValues);
			}
			
			for(FGS fgs:model.fields()){				
				Object value=fgs.getObject(model);
				fgs.setObject(x, value);
			}
 			 	
			return x;
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}
	  
	protected void doValidate(Model model) {
		String validate=DbProp.PROP_TABLE_VALIDATE.getValue(db,tableName);
		
		if(validate.equalsIgnoreCase("true") || validate.equals("1")){			
			List errors=validate(model);
			if(errors.size()>0){
				throw new RuntimeException(errors.toString());
			}
		}
	}
   
	
	/**
	 * 校验字段数据的是否合法.
	 * 
	 * @param model model for validate
	 * @return 不合法的字段列表{字段名: 错误信息}. 如果没有错误, 则为空列表.
	 */
	public List validate(Model model){
		if(validator==null){
			String clazz=DbProp.PROP_TABLE_VALIDATOR.getValue(db,tableName);
			
			if(MelpString.isEmpty(clazz)){
				validator=new Validator();
			}else{
				try{
					validator=(Validator)MelpClass.forName(clazz.trim()).newInstance();
				}catch(Exception e){
					throw new RuntimeException(e);
				}
			}
		}
		
		return validator.validate(model);
	}
	  
	
	public List uniqueIndexes(){
		List unique=new ArrayList();
		for(ModelIndex index:indexes){
			if(index.isUnique()){
				unique.add(index);
			}
		}		  
		return unique;
	}
	
	
	boolean iChanged(){
		return changed;
	}
	 

	protected static FGS createFGS(final MetaColumn c,final FGS mfd){	
		
		FGS fgs=new FGS(mfd==null?null:mfd.getType(),c.getJavaName(),c.getName()){
			private Column columnAnnotation;
			public void setObject(Object bean,Object v){
				if(mfd!=null){
					mfd.setObject(bean, v);
				}else{
					if(bean instanceof Model){
						Model m=(Model)bean;
						
						if(Date.class.getName().equals(c.getJavaType())){
							v=MelpClass.converter.convert(v, Date.class);
						}else{
							try{
								String jtype=c.getJavaType();
								if(jtype.indexOf(".")<0){
									jtype="java.lang."+jtype;
								}
								
								v=MelpClass.converter.convert(v, Class.forName(jtype));
							}catch(ClassNotFoundException e){
								throw new RuntimeException("Convert: "+v+" to class exception: "+c.getJavaType(),e);
							}
						}
						
						m.holder().set(c.getName(), v);							
					} 					
				}				
			}
			
			public Object getObject(Object bean){
				if(mfd!=null){
					return mfd.getObject(bean);
				}else{
					if(bean instanceof Model){
						Model m=(Model)bean;
						return m.holder().get(c.getName());
					}else{
						return null;
					}
				}
			}
			
			public Field getField(){
				return mfd==null?null:mfd.getField();
			}
			
			@SuppressWarnings("unchecked")
			public  T getAnnotation(Class annotationClass) {				
				if(annotationClass==Column.class){
					if(columnAnnotation==null){
						columnAnnotation=createColumn(c);
					}
					return (T)columnAnnotation;
				}else{
					return null;
				}
			}
		};
		
		return fgs;
	}
	
	protected static Column createColumn(final MetaColumn c){
		return new Column(){								 
			public Class annotationType() {
				return Column.class;
			}
								 
			public String value() {							 
				return c.getValue();
			}
			
			 
			public String table() {							 
				return c.getTable().getName();
			}
			
			 
			public String remarks() {							 
				return c.getRemarks();
			}
									 
			public boolean notnull() {
				 
				return c.isNotnull();
			}
						 
			public String name() {
				return c.getName();
			}
			 
			public int length() {				 
				return c.getLength();
			}
			  
			public boolean key() {
				return c.isKey();
			}
			 
			public int jdbcType() {				 
				return c.getJdbcType();
			}
					 
			public boolean auto() {				 
				return c.isAuto();
			}
		};
	}
	
	public static Table createTable(final String tableName,final Table modelTable){
		return new Table(){  
			public String name() {					 
				return tableName;
			}
			
			public String value() {					 
				return tableName;
			}
	 			
			public String remarks() {
				return modelTable.remarks();
			}
			
			public String[] primaryKeys(){
				return modelTable.primaryKeys();
			}
			
			public Index[] indexes(){
				return modelTable.indexes();
			}
			
			public Class annotationType() {
				return Table.class;
			}
		};
	}
	
	public static Table createTable(final String tableName,final String ...primaryKeys){
		Table tb=new Table(){ 
			public Class annotationType() {
				return Table.class;
			}
			 
			public String value() {				 
				return tableName;
			}

			 
			public String name() {				 
				return tableName;
			}
			 
			public String remarks() {				 
				return "";
			}
 
			public String[] primaryKeys() {				 
				return primaryKeys;
			}
			 
			public Index[] indexes() {				 
				return new Index[0];
			}			
		};
		
		return tb;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy