`

model自动生成对应crud sql语句

    博客分类:
  • java
阅读更多

前言:在写《来!认识一下强大的Annotation》的时候我说大家喜欢我就再写一篇详细介绍和一篇实例文章。

现在我兑现了我的承诺,并且写了2篇实例文章,感谢大家的支持和关注~

阅读此文前建议先看《来!认识一下强大的Annotation》《Annotation详细介绍》两篇文章。

另一篇实例《annotation实现数据映射》

 

1.本例我们做了生么?

  • 根据model的相关信息生成增删改查的sql语句(通用型的哦~)
  • 保证通用的前提是您的model必须使用DbInfo、Id、columns 三个annotation进行标记

2.步骤:

  1. 创建三个annotation(DbInfo、Id、columns)
  2. 创建User类,并用上面三个annotation对相应元素进行标记
  3. 创建我们自己的APT,让它读取User类的相关信息,并生成对应信息的sql语句
  4. 创建一个bat文件让其编译这些java文件(附件提供大家可以下载试用)
  5. 通用性测试,编写一个新的类来测试其通用性

3.说明:

  1. 本例实际上是在做一个APT(Annotation Processing Tool),采用继承AbstractProcessor实现process方法的方式实现。
  2. 执行的时候使用的是命令行 cmd进行执行,本例制作了一个批处理文件,大家直接运行就可以看到生成的sql文件。
  3. 本例抛砖引玉,顺着这个思路想 你可以自己做出一个辅助你今后工作的工具助手,不多说勒,自己动脑琢磨琢磨吧。
  4. 对程序不多做解释了,需要注意的地方,注释写的很清楚了。
  5. 程序存在不完善的地方,有兴趣的朋友可以自己进行完善。
    1.采用的是hashmap 所以遍历出来的字段顺序并不是想像中的顺序。
    2.本例能对单个类进行生成sql的操作,不能批量对多个类文件进行分析生成,有需要的可以顺着本文思路修改完善。
    3.本例对id的生成策略只实现了uuid的方式,可以自行扩展多种方式。
    4.insert语句的map遍历没有任何保证。
    5.并没有对无字段情况进行判断,可能会引发截字错误。

4.为什么我总爱说 抛砖引玉?

  1. 写一个完善的成型的程序 是需要花费大量时间的,这也是例子存在很多不完善的原因。
  2. 一个完善的程序势必会包含很多独特的业务逻辑和验证,会使得代码庞大且复杂 不利于学习。
  3. 所以我的文章总爱说 抛砖引玉,技术和思路分享给你,怎么用就是各位的事了~

    定义三个annotation(DbInfo、Id、columns)

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author cxy
 * 将annotation都定义在这个文件方便看
 */
public class AnnotationPool{}

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.TYPE) 
@interface DbInfo
{
	String url();  //数据库地址
	String un();  //数据库连接用户名
	String pw();  //数据库连接密码
	String tableName();  //model对应数据表
}

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.FIELD) 
@interface Id
{
	String column();  //数据库对应字段
	String describe();  //字段描述
	String generator() default "uuid";  //id生成方式,默认是uuid
}

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.FIELD) 
@interface columns
{
	String type();  //数据库类型
	String column();  //数据库对应字段
	int length() default 200;  //数据库字段长度
	String describe();  //字段描述
}

 

    定义User类,并用上面的三个annotation标记

/**
 * @author cxy
 */
@DbInfo(url="jdbc:mysql://localhost/dbtest",un="root",pw="root",tableName="t_test_user")
public class User
{
	@Id(column="id_",describe="唯一标识")
	private String id; 
	@columns(column="user_name_",describe="用户名",type="string")
	private String userName; 
	@columns(column="friend_num_",describe="好友数量",type="int",length=10)
	private int friendNum;
	
	public String getId()
	{
		return id;
	}
	public void setId(String id)
	{
		this.id = id;
	}
	public String getUserName()
	{
		return userName;
	}
	public void setUserName(String userName)
	{
		this.userName = userName;
	}
	public int getFriendNum()
	{
		return friendNum;
	}
	public void setFriendNum(int friendNum)
	{
		this.friendNum = friendNum;
	} 
}

 

    创建我们自己的APT,让它读取User类的相关信息,并生成对应信息的sql语句

import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;

@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({"DbInfo","Id","columns"})
public class DbAp extends AbstractProcessor
{
	Map<String,String> dbInfo=new HashMap<>();  //用来存储数据库相关信息
	Map<String,String> pkInfo=new HashMap<>(); //主键信息
	Map<String,Map<String,Object>> columnInfo=new HashMap<>(); //字段信息
	@Override
	public boolean process(Set<? extends TypeElement> annotations,
			RoundEnvironment roundEnv)
	{
		//它会循环处理每个程序对象,最后一个是空的用于结束的(不是我们主动创建的),我们这里不对其进行处理
		if(roundEnv.getRootElements().isEmpty()){ return false;} 
			
		String annotationName="";
		//遍历当前处理类的所有annotation
		for(TypeElement t:annotations)
		{
			annotationName="@"+t.getSimpleName().toString();
			//遍历被t(annotation)修饰的所有元素
			for(Element e : roundEnv.getElementsAnnotatedWith(t))
			{
				//System.out.println("当前annotation是:"+annotationName);
				//System.out.println("当前被修饰的元素是:"+e.getSimpleName());
				//System.out.println("----------------------");
				//获取数据库信息
				if(annotationName.equals("@DbInfo"))
				{
					dbInfo.put("url",e.getAnnotation(DbInfo.class).url());
					dbInfo.put("un",e.getAnnotation(DbInfo.class).un());
					dbInfo.put("pw",e.getAnnotation(DbInfo.class).pw());
					dbInfo.put("table",e.getAnnotation(DbInfo.class).tableName());
				}
				
				//获取主键信息
				if(annotationName.equals("@Id"))
				{
					pkInfo.put("t",jtd(e.getClass().getSimpleName())+"(32)");
					pkInfo.put("c",e.getAnnotation(Id.class).column());
					pkInfo.put("d",e.getAnnotation(Id.class).describe());
					pkInfo.put("u",e.getAnnotation(Id.class).generator());
				}
				
				//获取字段信息
				Map<String,Object> tempOne=null;
				if(annotationName.equals("@columns"))
				{
					tempOne =new HashMap<>();
					tempOne.put("t", jtd(e.getAnnotation(columns.class).type()));
					tempOne.put("c", e.getAnnotation(columns.class).column());
					tempOne.put("d", e.getAnnotation(columns.class).describe());
					tempOne.put("l", e.getAnnotation(columns.class).length());
					columnInfo.put(e.getSimpleName().toString(), tempOne);
				}
			}
		}
		
		System.out.println("annotation信息获取结束。");
		System.out.println(dbInfo);
		System.out.println(pkInfo);
		System.out.println(columnInfo);
		try
		{
			FileOutputStream fos=new FileOutputStream("mysql.sql");
			fos.write(createSql().toString().getBytes());
			fos.close();
		} catch (Exception e)
		{
			e.printStackTrace();
		}
		return true; //annotations是否被这个处理器声明,如果是的话随后的处理器将不再处理这些annotation
	}
	
	/**
	 * 创建数据表
	 * @throws Exception 
	 */
	public String createSql() throws Exception
	{
		String uuid=UUID.randomUUID().toString().replaceAll("-", "");
		StringBuilder sql= new StringBuilder("CREATE TABLE IF NOT EXISTS ");
		sql.append(dbInfo.get("table"));
		sql.append(" ( ");
		sql.append(pkInfo.get("c")+" "+pkInfo.get("t")+" NOT NULL UNIQUE PRIMARY KEY,");
		for(String one : columnInfo.keySet())
		{
			sql.append(one +" "+columnInfo.get(one).get("t")+"("+columnInfo.get(one).get("l")+"),");
		}
		sql.delete(sql.length()-1, sql.length());
		sql.append(" ); \r\n");
		
		//拼装insert语句
		sql.append("insert into "+dbInfo.get("table")+" ("+pkInfo.get("c")+",");
		for(String one : columnInfo.keySet())
		{
			sql.append(one+",");
		}
		sql.delete(sql.length()-1, sql.length());
		sql.append(") VALUES ('"+uuid+"'," );
		for(String one : columnInfo.keySet())
		{
			if(columnInfo.get(one).get("t").equals("varchar"))
			{
				sql.append("'?',");
			}
			if(columnInfo.get(one).get("t").equals("int"))
			{
				sql.append("?,");
			}
		}
		sql.delete(sql.length()-1, sql.length());
		sql.append( ");\r\n");
		
		//拼装删除语句
		sql.append("delete from "+dbInfo.get("table")+" where "+pkInfo.get("c")+"='"+uuid+"';\r\n");
		
		//拼装修改语句
		sql.append("UPDATE "+dbInfo.get("table")+" set ");
		for(String one : columnInfo.keySet())
		{
			if(columnInfo.get(one).get("t").equals("varchar"))
			{
				sql.append(one+"='?',");
			}
			if(columnInfo.get(one).get("t").equals("int"))
			{
				sql.append(one+"=?,");
			}
		}
		sql.delete(sql.length()-1, sql.length());
		sql.append(" where "+pkInfo.get("c")+"='"+uuid+"';\r\n");
		
		//查询语句
		sql.append("select ");
		for(String one : columnInfo.keySet())
		{
			sql.append(one+" as "+columnInfo.get(one).get("d")+",");
		}
		sql.delete(sql.length()-1, sql.length());
		sql.append(" from "+dbInfo.get("table"));
		return sql.toString();
	}
	
	
	/** javaTypeToDbType、java类型和数据库类型的转换
	 * @param type String
	 * @return VARCHAR
	 */
	public String jtd(String type)
	{
		if("String".equals(type))	return  "varchar";
		if("int".equals(type))	return  "int";
		//其他的自己扩展吧
		return  "varchar";
	}
}

 

    写一个bat文件来使用apt(附件有这个bat)

    
 

   最后写另一个测试类来测试其通用型

/** 文章类 用于测试apt的通用性
 * @author cxy
 */
@DbInfo(url="jdbc:mysql://localhost/dbtest",un="root",pw="root",tableName="t_test_article")
public class Article
{
	@Id(column="sid",describe="文章唯一标识")
	private String sid=""; //文章id
	@columns(column="title_",describe="标题",type="string")
	private String title="";
	@columns(column="content_",describe="内容",type="string",length=2000)
	private String content="";
	@columns(column="click_num_",describe="点击量",type="int")
	private int clickNum =0;
	public String getSid()
	{
		return sid;
	}
	public void setSid(String sid)
	{
		this.sid = sid;
	}
	public String getTitle()
	{
		return title;
	}
	public void setTitle(String title)
	{
		this.title = title;
	}
	public String getContent()
	{
		return content;
	}
	public void setContent(String content)
	{
		this.content = content;
	}
	public int getClickNum()
	{
		return clickNum;
	}
	public void setClickNum(int clickNum)
	{
		this.clickNum = clickNum;
	}
}

  

    结果图:



 

声明:

1.原创文章,转载请标明并加本文连接。

2.文章反映个人愚见,如有异议欢迎讨论指正

  • 大小: 48.2 KB
  • 大小: 70.2 KB
  • APT.rar (4.4 KB)
  • 下载次数: 47
4
0
分享到:
评论
3 楼 snkcxy 2014-04-21  
city_moon 写道
楼主的文章写得很好,赞一个!!

呵呵 谢谢
2 楼 city_moon 2014-04-17  
楼主的文章写得很好,赞一个!!
1 楼 LinApex 2013-04-27  
不错,强大,正想开发一款小型的Java数据持久称框架.有借鉴作用.

相关推荐

Global site tag (gtag.js) - Google Analytics