写在前面:

1、该框架为自己所写的第一个框架类产品,可能有着许多不足的地方,读者可以到评论区指出。同时,该微框架的源码也会开源至博客中,够后来的学习者借鉴。由于该框架逻辑结构稍些复杂,不可能花大量篇幅去逐一行代码的讲解,本文后半部分会贴出框架源代码,源代码中有着大量的注释,学习者若有看不懂的地方可以在博客后面留言。

2、该框架称之为微框架,在于其实现的功能简单,帮助开发者自动的建库建表,拜托对数据库操作的烦恼。众所周知,在javaee流行框架中mybatis的逆向工程生成的通用Mapper可以让开发者拜托对数据库增删改查语句的编写,but,开发者还需要自己对数据库进行建库建表,因此,也要求开发者掌握相应的数据库知识。使用本框架,框架将自动的为开发者建库建表,配合mybatis框架的通用mapper将实现开发者全程无需自己对数据库直接操作的开发体验。

3、开源地址 https://gitee.com/xue-guo-peng/MyOrm

1、框架的使用

1.1、导入框架jar包,并导入jdbc驱动,添加为项目库。

框架jar包下载地址:https://gitee.com/xue-guo-peng/MyOrm/releases

1.2、导入后的框架目录结构为:

1.3、将数据源的连接信息放到写到一个database.properties文件中,并放在src目录下:

其中的数据库的名字(如图上的aaa)可以填以存在的数据库,也可以填不存在的数据库,如果所填的数据库不存在,将会自动创建。

1.4、编写实体类,并在你需要生成表的实体类上标注注解

在Student类上,我们不标注注解,到时候框架扫描器就会排除该实体类。

在User类上,我们给表和字段标注注解,该实体类用于对数据表的生成:

  1. package com.xgp.company.model;
  2. import com.xgp.company.annotaion.MyField;
  3. import com.xgp.company.annotaion.MyTable;
  4. import com.xgp.company.annotaion.Plan;
  5. /**
  6. * 用户表
  7. */
  8. @MyTable
  9. public class User {
  10. //用户ID
  11. @MyField(plan = Plan.PK_AUTO,Comment = "这是用户id")
  12. private int id;
  13. //用户名
  14. @MyField(plan = Plan.NOT_NULL,len = 16,Comment = "这是用户名")
  15. private String username;
  16. //用户密码
  17. @MyField(plan = Plan.NOT_NULL,len = 32,Comment = "这是用户密码")
  18. private String password;
  19. //用户性别 1男 0女
  20. @MyField(Comment = "用户性别")
  21. private int sex = 1; //性别默认为1
  22. //用户年龄
  23. @MyField(Comment = "用户年龄")
  24. private int age = 20; //年龄默认为20
  25. //用户是否被删除
  26. @MyField(Comment = "用户的置废标识")
  27. private int is_del;
  28. public int getId() {
  29. return id;
  30. }
  31. public void setId(int id) {
  32. this.id = id;
  33. }
  34. public String getUsername() {
  35. return username;
  36. }
  37. public void setUsername(String username) {
  38. this.username = username;
  39. }
  40. public String getPassword() {
  41. return password;
  42. }
  43. public void setPassword(String password) {
  44. this.password = password;
  45. }
  46. public int getSex() {
  47. return sex;
  48. }
  49. public void setSex(int sex) {
  50. this.sex = sex;
  51. }
  52. public int getAge() {
  53. return age;
  54. }
  55. public void setAge(int age) {
  56. this.age = age;
  57. }
  58. public int getIs_del() {
  59. return is_del;
  60. }
  61. public void setIs_del(int is_del) {
  62. this.is_del = is_del;
  63. }
  64. }

说明:set方法不是不需的,get方法是必须的,因为在获取字段的默认值时是通过get方法反射获取的。一个实体类被框架的注解标示,框架会自动的获取类名、属性名、属性类型、以及=后面的放回值,结合注解上的信息,进行表的生成。

1.5、@MyTable注解的说明:

该注解作用于类上,只有一个value属性,可填可不填。填了就是生成数据表的表名,不填则会获取被标注的实体类类名作为表名。

1.6、@MyField注解的说明:

该注解作用于实体类的字段上

name属性为数据表的字段名字,可填可不填,不填则获取实体类的属性名字作为字段名。

len属性为字段的长度,默认为3

plan为字段的相关信息,默认能为NULL

commen为对字段的解释说明

1.6、Plan枚举类的说明:

说明见注释,已经很详细了。

1.7、创建main方法,调用框架

其中需要传递的参数为实体类所在的包名,根据所传的参数包名,框架的注解扫描器将会自动的扫描该包下被标注了@MyTable注解的所有实体类,并根据相应的信息,在数据库中建库建表。

1.8、点击运行,建库建表



如图,则库表创建成功,下面就对解析注解的三个工具类进行简要说明:

2、CreateTable类的说明

该类为反射获取类和注解上的信息工具类,拼接建表的sql语句。

create()函数用于创建表

flag()函数用于判断该实体类是否需要映射到数据库中

init()函数用于拼接建表的sql语句

getFilelds()函数用于反射获取实体类的注解信息和字段信息

getTableName()函数用于获取表的名字

该类代码如下:

  1. package com.xgp.company.tool;
  2. import com.xgp.company.annotaion.MyField;
  3. import com.xgp.company.annotaion.MyTable;
  4. import com.xgp.company.annotaion.Plan;
  5. import com.xgp.company.model.User;
  6. import java.lang.reflect.Field;
  7. import java.lang.reflect.InvocationTargetException;
  8. import java.lang.reflect.Method;
  9. import java.sql.Connection;
  10. import java.sql.SQLException;
  11. import java.sql.Statement;
  12. import java.util.ArrayList;
  13. import java.util.HashMap;
  14. import java.util.List;
  15. import java.util.Map;
  16. @SuppressWarnings("all")
  17. public class CreateTable {
  18. //获取数据库连接对象
  19. private static Connection con = JDBCUtilsConfig.getConnection();
  20. public static void create(String pack) {
  21. List<String> lists = ClazzUtils.getClazzName("com.xgp.company.model", true);
  22. for (String list : lists) {
  23. String newpack = pack + list.substring(list.lastIndexOf('.'));
  24. // System.out.println(newpack);
  25. Class<?> clazz = null;
  26. try {
  27. clazz = Class.forName(newpack);
  28. } catch (ClassNotFoundException e) {
  29. e.printStackTrace();
  30. }
  31. if(flag(clazz))
  32. init(clazz);
  33. }
  34. }
  35. private static boolean flag(Class clazz) {
  36. //通过反射获取MyTable注解
  37. //获得注解的value的值
  38. MyTable table = (MyTable) clazz.getAnnotation(MyTable.class);
  39. if(table == null) return false;
  40. return true;
  41. }
  42. private static void init(Class clazz) {
  43. // Class<User> clazz = User.class;
  44. String tableName = getTableName(clazz);
  45. List<Map<String, String>> filelds = null;
  46. filelds = getFilelds(clazz);
  47. /* for (Map<String, String> fileld : filelds) {
  48. for (String s : fileld.keySet()) {
  49. System.out.println(s + " === " + fileld.get(s));
  50. }
  51. }*/
  52. // 拼接sql语句
  53. String sqlTitle = "CREATE TABLE IF NOT EXISTS " + tableName + "(";
  54. String sql = sqlTitle;
  55. for (Map<String, String> fileld : filelds) {
  56. String def = fileld.get("def");
  57. if("VARCHAR".equals(fileld.get("type"))) {
  58. def = "DEFAULT" + " " +"'" + def + "'";
  59. }else if("PRIMARY KEY AUTO_INCREMENT".equals(fileld.get("plan")) || "PRIMARY KEY".equals(fileld.get("plan"))) {
  60. def = "";
  61. }else {
  62. def = "DEFAULT" + " " + def;
  63. }
  64. String sqlBody = fileld.get("name") + " " + fileld.get("type") + "(" + fileld.get("len") + ")" + " " + fileld.get("plan") + " " + def + " " + "COMMENT" + " '" + fileld.get("comment") + "',";
  65. sql = sql + sqlBody;
  66. }
  67. sql = sql.substring(0,sql.length() - 1) + ")ENGINE=INNODB DEFAULT CHARSET = 'utf8'";
  68. // System.out.println(sql);
  69. //执行sql语句
  70. Statement stmt = null;
  71. try {
  72. stmt = con.createStatement();
  73. } catch (SQLException e) {
  74. e.printStackTrace();
  75. }
  76. try {
  77. stmt.executeUpdate(sql);
  78. } catch (SQLException e) {
  79. e.printStackTrace();
  80. }
  81. try {
  82. stmt.close();
  83. } catch (SQLException e) {
  84. e.printStackTrace();
  85. }
  86. System.out.println(tableName + "数据表创建成功");
  87. }
  88. private static List<Map<String,String>> getFilelds(Class clazz){
  89. //获取类上的字段
  90. Field[] fields = clazz.getDeclaredFields();
  91. List<Map<String,String>> list = new ArrayList();
  92. for (Field field : fields) {
  93. MyField ano = field.getAnnotation(MyField.class);
  94. Map<String,String> map = new HashMap<>();
  95. String name = ano.name(); // 字段名称
  96. if(name.isEmpty()) name = field.getName();
  97. map.put("name",name); // 存字段名称
  98. int len = ano.len(); // 长度
  99. map.put("len",len + "");//存字段长度
  100. Plan plan = ano.plan(); //计划方案
  101. //分析计划
  102. switch (plan) {
  103. case PK: map.put("plan","PRIMARY KEY");break;
  104. case PK_AUTO:map.put("plan","PRIMARY KEY AUTO_INCREMENT");break;
  105. case NOT_NULL:map.put("plan","NOT NULL");break;
  106. case NULL:map.put("plan","NULL");break;
  107. }
  108. String comment = ano.Comment();//字段备注
  109. map.put("comment",comment); //存备注
  110. //获取字段类型
  111. String type = field.getGenericType().getTypeName();
  112. if ("int".equals(type)) {
  113. map.put("type","INT");
  114. }else if("java.lang.String".equals(type)) {
  115. map.put("type","VARCHAR");
  116. }
  117. //获取默认值
  118. Object user = null;
  119. try {
  120. user = clazz.newInstance();
  121. } catch (InstantiationException e) {
  122. e.printStackTrace();
  123. } catch (IllegalAccessException e) {
  124. e.printStackTrace();
  125. }
  126. Method getxxx = null;
  127. try {
  128. getxxx = clazz.getMethod("get" + name.substring(0, 1).toUpperCase() + name.substring(1));
  129. } catch (NoSuchMethodException e) {
  130. e.printStackTrace();
  131. }
  132. Object res = null;
  133. try {
  134. res = getxxx.invoke(user);
  135. } catch (IllegalAccessException e) {
  136. e.printStackTrace();
  137. } catch (InvocationTargetException e) {
  138. e.printStackTrace();
  139. }
  140. String def = "";
  141. if(res != null) def = res + "";
  142. // System.out.println(res);
  143. map.put("def",def);
  144. //存入外部集合
  145. list.add(map);
  146. }
  147. return list;
  148. }
  149. private static String getTableName(Class clazz) {
  150. //通过反射获取MyTable注解
  151. //获得注解的value的值
  152. MyTable table = (MyTable) clazz.getAnnotation(MyTable.class);
  153. String tableName = table.value();
  154. //创建表
  155. if(tableName .isEmpty())
  156. //获取类类名,用户没有自己定义就默认为表名
  157. tableName = clazz.getSimpleName();
  158. return tableName;
  159. }
  160. }

3、ClazzUtils该类用于拼接实体类所在包的.class文件的绝对物理路径,方便于框架的注解扫描器进行扫描

  1. package com.xgp.company.tool;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.net.JarURLConnection;
  5. import java.net.URL;
  6. import java.util.ArrayList;
  7. import java.util.Enumeration;
  8. import java.util.List;
  9. import java.util.jar.JarEntry;
  10. import java.util.jar.JarFile;
  11. public class ClazzUtils {
  12. private static final String CLASS_SUFFIX = ".class";
  13. private static final String CLASS_FILE_PREFIX = File.separator + "classes" + File.separator;
  14. private static final String PACKAGE_SEPARATOR = ".";
  15. /**
  16. 26
  17. * 查找包下的所有类的名字
  18. 27
  19. * @param packageName
  20. 28
  21. * @param showChildPackageFlag 是否需要显示子包内容
  22. 29
  23. * @return List集合,内容为类的全名
  24. 30
  25. */
  26. public static List<String> getClazzName(String packageName, boolean showChildPackageFlag ) {
  27. List<String> result = new ArrayList<>();
  28. String suffixPath = packageName.replaceAll("\\.", "/");
  29. ClassLoader loader = Thread.currentThread().getContextClassLoader();
  30. try {
  31. Enumeration<URL> urls = loader.getResources(suffixPath);
  32. while(urls.hasMoreElements()) {
  33. URL url = urls.nextElement();
  34. if(url != null) {
  35. String protocol = url.getProtocol();
  36. if("file".equals(protocol)) {
  37. String path = url.getPath();
  38. System.out.println(path);
  39. result.addAll(getAllClassNameByFile(new File(path), showChildPackageFlag));
  40. } else if("jar".equals(protocol)) {
  41. JarFile jarFile = null;
  42. try{
  43. jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
  44. } catch(Exception e){
  45. e.printStackTrace();
  46. }
  47. if(jarFile != null) {
  48. result.addAll(getAllClassNameByJar(jarFile, packageName, showChildPackageFlag));
  49. }
  50. }
  51. }
  52. }
  53. } catch (IOException e) {
  54. e.printStackTrace();
  55. }
  56. return result;
  57. }
  58. private static List<String> getAllClassNameByFile(File file, boolean flag) {
  59. List<String> result = new ArrayList<>();
  60. if(!file.exists()) {
  61. return result;
  62. }
  63. if(file.isFile()) {
  64. String path = file.getPath();
  65. // 注意:这里替换文件分割符要用replace。因为replaceAll里面的参数是正则表达式,而windows环境中File.separator="\\"的,因此会有问题
  66. if(path.endsWith(CLASS_SUFFIX)) {
  67. path = path.replace(CLASS_SUFFIX, "");
  68. // 从"/classes/"后面开始截取
  69. String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())
  70. .replace(File.separator, PACKAGE_SEPARATOR);
  71. if(-1 == clazzName.indexOf("$")) {
  72. result.add(clazzName);
  73. }
  74. }
  75. return result;
  76. } else {
  77. File[] listFiles = file.listFiles();
  78. if(listFiles != null && listFiles.length > 0) {
  79. for (File f : listFiles) {
  80. if(flag) {
  81. result.addAll(getAllClassNameByFile(f, flag));
  82. } else {
  83. if(f.isFile()){
  84. String path = f.getPath();
  85. if(path.endsWith(CLASS_SUFFIX)) {
  86. path = path.replace(CLASS_SUFFIX, "");
  87. // 从"/classes/"后面开始截取
  88. String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())
  89. .replace(File.separator, PACKAGE_SEPARATOR);
  90. if(-1 == clazzName.indexOf("$")) {
  91. }
  92. }
  93. }
  94. }
  95. }
  96. }
  97. return result;
  98. }
  99. }
  100. private static List<String> getAllClassNameByJar(JarFile jarFile, String packageName, boolean flag) {
  101. List<String> result = new ArrayList<>();
  102. Enumeration<JarEntry> entries = jarFile.entries();
  103. while(entries.hasMoreElements()) {
  104. JarEntry jarEntry = entries.nextElement();
  105. String name = jarEntry.getName();
  106. // 判断是不是class文件
  107. if(name.endsWith(CLASS_SUFFIX)) {
  108. name = name.replace(CLASS_SUFFIX, "").replace("/", ".");
  109. if(flag) {
  110. // 如果要子包的文件,那么就只要开头相同且不是内部类就ok
  111. if(name.startsWith(packageName) && -1 == name.indexOf("$")) {
  112. result.add(name);
  113. }
  114. } else {
  115. // 如果不要子包的文件,那么就必须保证最后一个"."之前的字符串和包名一样且不是内部类
  116. if(packageName.equals(name.substring(0, name.lastIndexOf("."))) && -1 == name.indexOf("$")) {
  117. result.add(name);
  118. }
  119. }
  120. }
  121. }
  122. return result;
  123. }
  124. public static void main(String[] args) {
  125. List<String> list = ClazzUtils.getClazzName("com.xgp.company.model", true);
  126. for (String string : list) {
  127. System.out.println(string);
  128. }
  129. }
  130. }

4、JDBCUtilsConfig该类用于读取配置文件,获得与数据库的连接,并完成建库操作

  1. package com.xgp.company.tool;
  2. /**
  3. * JDBC工具类,读取配置文件
  4. */
  5. import java.io.IOException;
  6. import java.io.InputStream;
  7. import java.sql.*;
  8. import java.util.Properties;
  9. public class JDBCUtilsConfig {
  10. //定义的连接对象
  11. private static Connection con;
  12. //驱动
  13. private static final String dirverClass = "com.mysql.jdbc.Driver";
  14. //数据库的地址
  15. private static String url;
  16. //数据库的用户名
  17. private static String username;
  18. //数据库的密码
  19. private static String password;
  20. //静态代码块读取配置文件
  21. static {
  22. //读取配置文件
  23. readConfig();
  24. try {
  25. Class.forName(dirverClass);
  26. } catch (ClassNotFoundException e) {
  27. e.printStackTrace();
  28. }
  29. try {
  30. String newUrl = url.substring(0,url.lastIndexOf('/')) + "/";
  31. // System.out.println(newUrl);
  32. con = DriverManager.getConnection(newUrl,username,password);
  33. //创建数据库
  34. Statement stmt = con.createStatement();
  35. int end = url.indexOf('&');
  36. String data = null;
  37. if(end == -1) {
  38. data = url.substring(url.lastIndexOf('/') + 1);
  39. }else {
  40. data = url.substring(url.lastIndexOf('/') + 1,end);
  41. }
  42. String sql = "CREATE DATABASE IF NOT EXISTS " + data +" DEFAULT CHARACTER SET = 'utf8' ";
  43. stmt.executeUpdate(sql);
  44. stmt.close();
  45. System.out.println(data + "数据库创建成功");
  46. //重新获取连接
  47. con = DriverManager.getConnection(url,username,password);
  48. } catch (SQLException e) {
  49. e.printStackTrace();
  50. }
  51. }
  52. private static void readConfig() {
  53. //使用反射技术获取文件的输入流
  54. InputStream in = JDBCUtilsConfig.class.getClassLoader().getResourceAsStream("database.properties");
  55. //解析properties的类
  56. Properties pro = new Properties();
  57. try {
  58. //读流
  59. pro.load(in);
  60. } catch (IOException e) {
  61. e.printStackTrace();
  62. }
  63. //获取值
  64. username = pro.getProperty("username");
  65. url = pro.getProperty("url");
  66. password = pro.getProperty("password");
  67. }
  68. //返回连接对象
  69. public static Connection getConnection() {
  70. return con;
  71. }
  72. //测试是否连接数据库成功
  73. public static void main(String[] args) {
  74. Connection con = JDBCUtilsConfig.getConnection();
  75. System.out.println(con);
  76. }
  77. }

5、思考

通过本次对类表映射微框架的编写,体会到反射技术的重要性。本框架中,采用了大量的反射技术,然后反射技术是会严重影响性能的。因此,在后期web开发中的Spring、mybatis、SpringMvc等框架中,是否会着更多的反射技术的使用?而这些框架给我们带来快捷开发的同时,是否也在一定程度上影响了系统的性能呢?

java基础复习-自定义注解4(结合JDBC技术,打造类表映射微框架)的更多相关文章

  1. Java基础笔记 – Annotation注解的介绍和使用 自定义注解

    Java基础笔记 – Annotation注解的介绍和使用 自定义注解 本文由arthinking发表于5年前 | Java基础 | 评论数 7 |  被围观 25,969 views+ 1.Anno ...

  2. Java基础教程:注解

    Java基础教程:注解 本篇文章参考的相关资料链接: 维基百科:https://zh.wikipedia.org/wiki/Java%E6%B3%A8%E8%A7%A3 注解基础与高级应用:http: ...

  3. Java基础复习笔记系列 九 网络编程

    Java基础复习笔记系列之 网络编程 学习资料参考: 1.http://www.icoolxue.com/ 2. 1.网络编程的基础概念. TCP/IP协议:Socket编程:IP地址. 中国和美国之 ...

  4. Java基础复习笔记系列 八 多线程编程

    Java基础复习笔记系列之 多线程编程 参考地址: http://blog.csdn.net/xuweilinjijis/article/details/8878649 今天的故事,让我们从上面这个图 ...

  5. Java基础复习笔记系列 七 IO操作

    Java基础复习笔记系列之 IO操作 我们说的出入,都是站在程序的角度来说的.FileInputStream是读入数据.?????? 1.流是什么东西? 这章的理解的关键是:形象思维.一个管道插入了一 ...

  6. Java基础复习笔记系列 五 常用类

    Java基础复习笔记系列之 常用类 1.String类介绍. 首先看类所属的包:java.lang.String类. 再看它的构造方法: 2. String s1 = “hello”: String ...

  7. Java基础复习笔记系列 四 数组

    Java基础复习笔记系列之 数组 1.数组初步介绍? Java中的数组是引用类型,不可以直接分配在栈上.不同于C(在Java中,除了基础数据类型外,所有的类型都是引用类型.) Java中的数组在申明时 ...

  8. Java基础复习笔记基本排序算法

    Java基础复习笔记基本排序算法 1. 排序 排序是一个历来都是很多算法家热衷的领域,到现在还有很多数学家兼计算机专家还在研究.而排序是计算机程序开发中常用的一种操作.为何需要排序呢.我们在所有的系统 ...

  9. 《Java基础复习》-控制执行流程

    最近任务太多了,肝哭我了,boom 参考书目:Thinking in Java <Java基础复习>-控制执行流程 Java使用了C的所有流程控制语句 涉及关键字:if-else.whil ...

随机推荐

  1. 【转】直播流程,视频推流,视频拉流,简介,SMTP、RTMP、HLS、 PLPlayerKit

    原:https://www.cnblogs.com/baitongtong/p/11248966.html 1 .音视频处理的一般流程: 数据采集→数据编码→数据传输(流媒体服务器) →解码数据→播放 ...

  2. python学习HTML之CSS

    1.sytle属性设置 . <head> <meta charset="UTF-8"> <title>Title</title> & ...

  3. VBA 学习笔记 - 日期

    date() 返回当前的系统日期 返回格式为 YYYY/MM/DD CDate() 学习资料:https://www.yiibai.com/vba/vba_cdate_function.html 将有 ...

  4. [0CTF 2016] piapiapia

    一道非常有意思的反序列化漏洞的题目 花费了我不少时间理解和记忆 这里简单记录其中精髓 首先打开是一个登陆页面 dirsearch扫描到了www.zip源码备份 update.php <?php ...

  5. phpstudy所需运行库

    百度云链接:http://pan.baidu.com/s/1jIu0v7C 密码:j1qu

  6. mysql数据库数据备份还原

    1.直接在命令行里面执行 备份一个数据库:mysqldump -h server -u username -p password db_name > database-sqlbkp_`date ...

  7. windows下如何快速删除大文件

    rmdir  磁盘:\文件夹的名字  /s /q; eg:rmdir E:\vue_workspace\KB\day08    /s/q /S 表示除目录本身外,还将删除指定目录下的所有子目录和文件. ...

  8. 吴裕雄--天生自然Numpy库学习笔记:Numpy 数组操作

    import numpy as np a = np.arange(8) print ('原始数组:') print (a) print ('\n') b = a.reshape(4,2) print ...

  9. 吴裕雄 python 神经网络——TensorFlow训练神经网络:MNIST最佳实践

    import os import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data INPUT_N ...

  10. Java IO流详解(四)——字符流Reader和Writer

    前面一章介绍了字节流的使用,提到了字节流在处理utf-8编码的中文可能会出现乱码的情况(其他编码的中文同样会出现乱码),所以Java针对这一情况提供了字符流. 但是字符流只能处理字符,不能用来处理 . ...