`

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数据持久称框架.有借鉴作用.

相关推荐

    Mybatis plus 基于 springBoot 源码

    Mybatis-Plus(简称MP)是一个 Mybatis... 内置性能分析插件:可输出Sql语句以及其执行时间,建议开发测试时启用该功能,能有效解决慢查询 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,预防误操作

    godbi:SQL数据库的通用DBI(数据库抽象类)

    检查godoc的定义: 用法分为三个级别: 基本:对原始SQL语句和存储过程进行操作。 Model :像MVC模式中的Model一样,在特定的表上操作并执行CRUD操作。 模式:在整个数据库模式上运行,并执行RESTful和GraphQL操作...

    支持多数据库的ORM框架ef-orm.zip

    但是这些SQL语句并不是直接传送给JDBC驱动的,而是 有着一个数据库方言层,经过方言层处理的SQL语句,就具备了在当前数据库上正确操作的能力。这相当于提供了一种能跨数据库操作的SQL语言。(E-SQL) E-SQL不但解决了...

    express-mysql-demo:基于node.js + express + mysql实现的restful风格的CRUD简单示例

    表达MySQL的演示 项目介绍 基于node.js + express + mysql实现的restful风格的CRUD...│ └── userSqlMap.js -- SQL语句封装 ├── model │ └── result.js -- 返回结果对象封装 ├── package.json -- 依赖模块

    magento开发教程

    1.Magento的配置系统 4 1.1设置组件的目录结构 4 1.2创建模块逻辑 6 1.3配置文件分析 7 1.4配置文件的作用 8 ...9.5比较运算符,构造Sql语句 86 9.6总结 89 10.技能考核 89 10.1理论考核 89 10.2实战考核 89

    node-mysql-db:MySQL的简单包装器,用于执行常见和复杂的任务,例如承诺查询和流式传输大型结果集

    节点mysql数据库该库是一个基本的查询生成器,并假定您要为基本CRUD操作之外的任何内容编写SQL。 它是围绕的轻量级包装,提供了基于promise的API,并支持流式传输大型结果集以进行处理。为什么? 我用多种语言编写...

    asp.net知识库

    直接从SQL语句问题贴子数据建表并生成建表语句的存储过程 从SQL中的一个表中导出HTML文件表格 获取數据库表的前N条记录 几段SQL Server语句和存储过程 生成表中的数据的脚本 最详细的SQL注入相关的命令整理 Oracle ...

    基于J2EE框架的个人博客系统项目毕业设计论文(源码和论文)

    1、 Struts是一个为开发基于模型(Model)-视图(View)-控制器(Controller)(MVC)模式的应用架构的开源框架,是利用Servlet,JSP和custom tag library构建Web应用的一项非常有用的技术。由于Struts能充分满足应用开发...

    Visual.Basic.2010.&.NET4.高级编程(第6版)-文字版.pdf

    11.4 从模型中生成数据库 457 11.5 小结 460 第12章 使用sql server 461 12.1 sql server compact 462 12.1.1 连接sql server compactedition数据库 463 12.1.2 同步数据 466 12.2 sql server内置的xml...

Global site tag (gtag.js) - Google Analytics