/*
* JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.geequery.codegen;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.persistence.Table;
import jef.codegen.support.OverWrittenMode;
import jef.codegen.support.RegexpNameFilter;
import jef.common.log.LogUtil;
import jef.database.DataObject;
import jef.database.DbUtils;
import jef.database.annotation.PartitionKey;
import jef.database.annotation.PartitionTable;
import jef.database.dialect.AbstractDialect;
import jef.database.dialect.ColumnType;
import jef.database.dialect.ColumnType.AutoIncrement;
import jef.database.dialect.ColumnType.GUID;
import jef.database.dialect.ColumnType.Varchar;
import jef.database.dialect.DatabaseDialect;
import jef.database.meta.Column;
import jef.database.meta.TableInfo;
import jef.database.routing.function.KeyFunction;
import jef.http.client.support.CommentEntry;
import jef.tools.ArrayUtils;
import jef.tools.Assert;
import jef.tools.StringUtils;
import com.github.geequery.codegen.Metadata.ColumnEx;
import com.github.geequery.codegen.ast.JavaAnnotation;
import com.github.geequery.codegen.ast.JavaConstructor;
import com.github.geequery.codegen.ast.JavaContainer;
import com.github.geequery.codegen.ast.JavaField;
import com.github.geequery.codegen.ast.JavaUnit;
/**
* 从数据库表生成JEF的Entity类
* 指定数据库表,自动生成表对应的DataObject.
* @author Administrator
*/
public class EntityGenerator {
private String basePackage="jef.generated.dataobject";
private DatabaseDialect profile;
private String entityBaseClass=DataObject.class.getName();
private MetaProvider provider;
private File srcFolder=new File("src1");
private String includePattern;
private String[] excludePatter;
private int maxTables = 900;
private EntityProcessorCallback callback;
public File generateOne(String tablename,String entityName,String tableComment) throws SQLException {
File file= this.saveJavaSource(generateSource(tablename,entityName,tableComment));
LogUtil.show(file.getAbsolutePath()+" generated.");
return file;
}
public String getEntityBaseClass() {
return entityBaseClass;
}
public void setEntityBaseClass(String entityBaseClass) {
this.entityBaseClass = entityBaseClass;
}
public JavaUnit generateSource(String tablename,String entityName,String tableComment) throws SQLException {
if(profile==null){
LogUtil.warn("Db dialect not set,default dialect set to Oracle.");
profile=AbstractDialect.getProfile("oracle");
}
tablename=profile.getObjectNameToUse(tablename);
//System.out.println(" Generating Class for table:"+tablename+"....");
if(entityName==null)entityName=DbUtils.underlineToUpper(tablename, true);
JavaUnit java=new JavaUnit(basePackage,entityName);
java.addComments("This class was generated by JEF according to the table in database.");
java.addComments("You need to modify the type of primary key field, to the strategy your own.");
if(StringUtils.isNotEmpty(tableComment)){
java.addComments("Table:"+tableComment);
}
JavaAnnotation anno=new JavaAnnotation(Table.class);
anno.put("name", tablename);
String schema=provider.getSchema();
if(StringUtils.isNotEmpty(schema)){
anno.put("schema", schema);
}
java.setAnnotation("@Entity",anno.toCode(java));
Metadata meta=provider.getTableMetadata(tablename);
if(meta.getCustParams()!=null && meta.getCustParams().get("custAnnot") != null){
String _tmpCustAnnot = meta.getCustParams().get("custAnnot");
java.addAnnotation(_tmpCustAnnot);
if(_tmpCustAnnot.indexOf("PartitionTable")>=0){
java.addImport(PartitionTable.class);
java.addImport(PartitionKey.class);
}
if(_tmpCustAnnot.indexOf("KeyFunction")>=0){
java.addImport(KeyFunction.class);
}
}
java.setExtends(entityBaseClass);
Map<String,String> pkColumns =meta.getPkFieldAndColumnNames();
if(pkColumns.size()>0){
java.addImport(javax.persistence.Id.class);
}
if(callback!=null){
callback.init(meta,tablename,provider.getSchema(),tableComment,java);
}
//生成元模型
JavaContainer enumField=new JavaContainer("public enum Field implements jef.database.Field{","}");
enumField.setWrap(false);
java.addRawBlock(enumField);
//生成用于元模型的字段配置
List<JavaField> pkFields=new ArrayList<JavaField>();
for(Column c:meta.getColumns()){
String columnName=c.getColumnName();
boolean isPk=pkColumns.containsValue(columnName);
ColumnType columnType;
try{
columnType=c.toColumnType(profile);
}catch(Exception e){
throw new RuntimeException("The column ["+tablename+":"+columnName+"] 's type is error:"+ e.getMessage());
}
if(!isPk){//不是主键时,错误的配置要还原
if(columnType.getClass()==ColumnType.AutoIncrement.class){
columnType=((AutoIncrement)columnType).toNormalType();
}else if(columnType.getClass()==ColumnType.GUID.class){
columnType=((GUID)columnType).toNormalType();
}
}
if(pkColumns.size()==1 && isPk){ //如果是单一主键,则修改为特定的JEF字段类型
if(columnType.getClass()==ColumnType.Int.class){
if(c.getColumnSize()==0)c.setColumnSize(8);
columnType=new ColumnType.AutoIncrement(c.getColumnSize());
}else if(columnType.getClass()==ColumnType.Varchar.class){
if(((Varchar)columnType).getLength()>=32){
columnType=new ColumnType.GUID();
}
}
columnType.setNullable(false); //主键列不允许为空
}
String[] annonation=getFieldAnnotation(java,columnName,columnType,isPk,c);
String initValue=null;
Class<?> clz=null;
String fieldName;
if(c instanceof ColumnEx){
fieldName=((ColumnEx) c).getFieldName();
clz=((ColumnEx) c).getJavaType();
initValue=((ColumnEx) c).getInitValue();
}else{
fieldName=columnToField(columnName);
}
if(clz==null)clz=columnType.getDefaultJavaType();
JavaField field=new JavaField(clz, fieldName);
field.setModifiers(Modifier.PRIVATE);
field.addAnnotation(annonation);
field.addComments(StringUtils.trimToNull(c.getRemarks()));
if(initValue!=null)field.setInitValue(initValue);
java.addFieldWithGetterAndSetter(field);
if(isPk)pkFields.add(field);
if(enumField.contentSize()>0){
enumField.addContent(","+field.getName());
}else{
enumField.addContent(field.getName());
}
if(callback!=null){
callback.addField(java,field,c,columnType);
}
}
java.addImport(javax.persistence.Column.class);
java.addImport(javax.persistence.Entity.class);
//生成默认的构造器和主键构造器
JavaConstructor c1=new JavaConstructor();
java.addMethod(c1.getKey(),c1);
if(!pkFields.isEmpty()){
JavaConstructor c2=new JavaConstructor();
for(JavaField field:pkFields){
c2.addparam(field.getType(), field.getName(),0);
c2.addContent(StringUtils.concat("this.",field.getName()," = ", field.getName(),";"));
}
java.addMethod(c2.getKey(),c2);
}
if(callback!=null){
callback.finish(java);
}
return java;
}
private String columnToField(String columnName) {
if(callback==null){
return DbUtils.underlineToUpper(columnName.toLowerCase(),false);
}else{
return callback.columnToField(columnName);
}
}
public File saveJavaSource(JavaUnit java){
try {
if(srcFolder!=null){
File f=java.saveToSrcFolder(srcFolder,"UTF-8",OverWrittenMode.AUTO);
return f;
}
} catch (IOException e) {
LogUtil.exception(e);
}
return null;
}
//生成默认的annonation
protected String[] getFieldAnnotation(JavaUnit java,String columnName,ColumnType column,boolean isPk,Column columnDef) {
List<String> result=new ArrayList<String>();
Map<String,Object> anno=column.toJpaAnnonation();
Boolean b = null;
if(columnDef instanceof ColumnEx){
ColumnEx ex=(ColumnEx)columnDef;
b=ex.getGenerated();
String customAnno=StringUtils.trimToNull(ex.getAnnotation());
if(customAnno!=null)
result.add(customAnno);
}
boolean isGenerated=false;
StringBuilder sb=new StringBuilder();
for(Iterator<String> iter=anno.keySet().iterator();iter.hasNext();){
String key=iter.next();
if(key.startsWith("@")){
Object v=anno.get(key);
String annoStr=(v==null)?key:key+"("+v+")";
if("@GeneratedValue".equals(key)){
if(Boolean.FALSE.equals(b)){//强行不做generate
continue;
}else{
java.addImport(javax.persistence.GenerationType.class);
java.addImport(javax.persistence.GeneratedValue.class);
}
isGenerated=true;
}else if("@Lob".equals(key)){
java.addImport(javax.persistence.Lob.class);
}
result.add(annoStr);
}else{
if(sb.length()>0)sb.append(",");
sb.append(key).append("=");
Object v=anno.get(key);
if(Number.class.isAssignableFrom(v.getClass())){
sb.append(anno.get(key));
}else if(Boolean.class==v.getClass()){
sb.append(v.toString());
}else{
sb.append("\"").append(anno.get(key)).append("\"");
}
}
}
//如果强行要求generated
Class<?> type=column.getDefaultJavaType();
if(Boolean.TRUE.equals(b) && isGenerated==false){
java.addImport(javax.persistence.GenerationType.class);
java.addImport(javax.persistence.GeneratedValue.class);
if(type==String.class){
result.add("@GeneratedValue(strategy=GenerationType.IDENTITY)");
}else if(type==Long.class || type==Integer.class || type==Integer.TYPE || type==Long.TYPE){
result.add("@GeneratedValue(strategy=GenerationType.SEQUENCE)");
}
}
if(isPk){
if(!result.contains("@Id")){
result.add("@Id");
}
}
result.add("@Column(name=\""+columnName+"\","+sb.toString()+")");
return result.toArray(new String[result.size()]);
}
public void generateSchema() throws SQLException{
Assert.notNull(provider);
RegexpNameFilter filter=new RegexpNameFilter(includePattern,excludePatter);
int n=0;
List<TableInfo> tables=provider.getTables();
if(callback!=null){
callback.setTotal(tables.size());
}
for(TableInfo table:tables){
String tableName=table.getName();
if(filter.accept(tableName)){
try {
generateOne(tableName,null,table.getRemarks());
} catch (SQLException e) {
LogUtil.exception(e);
}
n++;
}
if(n>=maxTables)break;
}
LogUtil.show(n+" Class Mapping to Table are generated.");
}
public void generate(CommentEntry... strings) {
Assert.notNull(provider);
int limit=500;
int n=0;
for(CommentEntry table: strings){
try {
generateOne(table.getKey(),null,table.getValue());
} catch (SQLException e) {
LogUtil.exception(e);
}
n++;
if(n>=limit)break;
}
LogUtil.show(strings.length+" Class Mapping to Table are generated.");
}
public void setProfile(DatabaseDialect profile) {
this.profile = profile;
}
public void setSrcFolder(File srcFolder) {
this.srcFolder = srcFolder;
}
public void setProvider(MetaProvider provider) {
this.provider = provider;
}
public void setIncludePattern(String includePattern) {
this.includePattern = includePattern;
}
public void addExcludePatter(String pattern) {
if(pattern==null){
this.excludePatter=new String[]{pattern};
}
this.excludePatter = (String[]) ArrayUtils.add(this.excludePatter, pattern);
}
public int getMaxTables() {
return maxTables;
}
public void setMaxTables(int maxTables) {
this.maxTables = maxTables;
}
public String getBasePackage() {
return basePackage;
}
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
public void setCallback(EntityProcessorCallback callback) {
this.callback = callback;
}
}