写在前面:

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类上,我们给表和字段标注注解,该实体类用于对数据表的生成:

package com.xgp.company.model;

import com.xgp.company.annotaion.MyField;
import com.xgp.company.annotaion.MyTable;
import com.xgp.company.annotaion.Plan; /**
* 用户表
*/
@MyTable
public class User { //用户ID
@MyField(plan = Plan.PK_AUTO,Comment = "这是用户id")
private int id; //用户名
@MyField(plan = Plan.NOT_NULL,len = 16,Comment = "这是用户名")
private String username; //用户密码
@MyField(plan = Plan.NOT_NULL,len = 32,Comment = "这是用户密码")
private String password; //用户性别 1男 0女
@MyField(Comment = "用户性别")
private int sex = 1; //性别默认为1 //用户年龄
@MyField(Comment = "用户年龄")
private int age = 20; //年龄默认为20 //用户是否被删除
@MyField(Comment = "用户的置废标识")
private int is_del; public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public int getSex() {
return sex;
} public void setSex(int sex) {
this.sex = sex;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public int getIs_del() {
return is_del;
} public void setIs_del(int is_del) {
this.is_del = is_del;
}
}

说明: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()函数用于获取表的名字

该类代码如下:

package com.xgp.company.tool;

import com.xgp.company.annotaion.MyField;
import com.xgp.company.annotaion.MyTable;
import com.xgp.company.annotaion.Plan;
import com.xgp.company.model.User; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; @SuppressWarnings("all")
public class CreateTable {
//获取数据库连接对象
private static Connection con = JDBCUtilsConfig.getConnection(); public static void create(String pack) {
List<String> lists = ClazzUtils.getClazzName("com.xgp.company.model", true); for (String list : lists) {
String newpack = pack + list.substring(list.lastIndexOf('.'));
// System.out.println(newpack);
Class<?> clazz = null;
try {
clazz = Class.forName(newpack);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if(flag(clazz))
init(clazz);
}
} private static boolean flag(Class clazz) {
//通过反射获取MyTable注解
//获得注解的value的值
MyTable table = (MyTable) clazz.getAnnotation(MyTable.class);
if(table == null) return false;
return true;
} private static void init(Class clazz) { // Class<User> clazz = User.class; String tableName = getTableName(clazz);
List<Map<String, String>> filelds = null;
filelds = getFilelds(clazz); /* for (Map<String, String> fileld : filelds) {
for (String s : fileld.keySet()) {
System.out.println(s + " === " + fileld.get(s));
}
}*/ // 拼接sql语句
String sqlTitle = "CREATE TABLE IF NOT EXISTS " + tableName + "("; String sql = sqlTitle;
for (Map<String, String> fileld : filelds) {
String def = fileld.get("def");
if("VARCHAR".equals(fileld.get("type"))) {
def = "DEFAULT" + " " +"'" + def + "'";
}else if("PRIMARY KEY AUTO_INCREMENT".equals(fileld.get("plan")) || "PRIMARY KEY".equals(fileld.get("plan"))) {
def = "";
}else {
def = "DEFAULT" + " " + def;
}
String sqlBody = fileld.get("name") + " " + fileld.get("type") + "(" + fileld.get("len") + ")" + " " + fileld.get("plan") + " " + def + " " + "COMMENT" + " '" + fileld.get("comment") + "',"; sql = sql + sqlBody;
}
sql = sql.substring(0,sql.length() - 1) + ")ENGINE=INNODB DEFAULT CHARSET = 'utf8'";
// System.out.println(sql); //执行sql语句
Statement stmt = null;
try {
stmt = con.createStatement();
} catch (SQLException e) {
e.printStackTrace();
}
try {
stmt.executeUpdate(sql);
} catch (SQLException e) {
e.printStackTrace();
}
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println(tableName + "数据表创建成功");
} private static List<Map<String,String>> getFilelds(Class clazz){
//获取类上的字段
Field[] fields = clazz.getDeclaredFields();
List<Map<String,String>> list = new ArrayList();
for (Field field : fields) {
MyField ano = field.getAnnotation(MyField.class); Map<String,String> map = new HashMap<>(); String name = ano.name(); // 字段名称
if(name.isEmpty()) name = field.getName();
map.put("name",name); // 存字段名称
int len = ano.len(); // 长度
map.put("len",len + "");//存字段长度
Plan plan = ano.plan(); //计划方案 //分析计划
switch (plan) {
case PK: map.put("plan","PRIMARY KEY");break;
case PK_AUTO:map.put("plan","PRIMARY KEY AUTO_INCREMENT");break;
case NOT_NULL:map.put("plan","NOT NULL");break;
case NULL:map.put("plan","NULL");break;
} String comment = ano.Comment();//字段备注
map.put("comment",comment); //存备注 //获取字段类型
String type = field.getGenericType().getTypeName();
if ("int".equals(type)) {
map.put("type","INT");
}else if("java.lang.String".equals(type)) {
map.put("type","VARCHAR");
}
//获取默认值
Object user = null;
try {
user = clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Method getxxx = null;
try {
getxxx = clazz.getMethod("get" + name.substring(0, 1).toUpperCase() + name.substring(1));
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
Object res = null;
try {
res = getxxx.invoke(user);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
String def = "";
if(res != null) def = res + "";
// System.out.println(res);
map.put("def",def);
//存入外部集合
list.add(map);
} return list;
} private static String getTableName(Class clazz) {
//通过反射获取MyTable注解
//获得注解的value的值
MyTable table = (MyTable) clazz.getAnnotation(MyTable.class);
String tableName = table.value(); //创建表
if(tableName .isEmpty())
//获取类类名,用户没有自己定义就默认为表名
tableName = clazz.getSimpleName(); return tableName;
}
}

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

package com.xgp.company.tool;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile; public class ClazzUtils {
private static final String CLASS_SUFFIX = ".class";
private static final String CLASS_FILE_PREFIX = File.separator + "classes" + File.separator;
private static final String PACKAGE_SEPARATOR = "."; /**
26
* 查找包下的所有类的名字
27
* @param packageName
28
* @param showChildPackageFlag 是否需要显示子包内容
29
* @return List集合,内容为类的全名
30
*/
public static List<String> getClazzName(String packageName, boolean showChildPackageFlag ) {
List<String> result = new ArrayList<>();
String suffixPath = packageName.replaceAll("\\.", "/");
ClassLoader loader = Thread.currentThread().getContextClassLoader();
try {
Enumeration<URL> urls = loader.getResources(suffixPath);
while(urls.hasMoreElements()) {
URL url = urls.nextElement();
if(url != null) {
String protocol = url.getProtocol();
if("file".equals(protocol)) {
String path = url.getPath();
System.out.println(path);
result.addAll(getAllClassNameByFile(new File(path), showChildPackageFlag));
} else if("jar".equals(protocol)) {
JarFile jarFile = null;
try{
jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
} catch(Exception e){
e.printStackTrace();
}
if(jarFile != null) {
result.addAll(getAllClassNameByJar(jarFile, packageName, showChildPackageFlag));
}
}
}
}
} catch (IOException e) { e.printStackTrace();
}
return result; } private static List<String> getAllClassNameByFile(File file, boolean flag) {
List<String> result = new ArrayList<>();
if(!file.exists()) {
return result;
}
if(file.isFile()) {
String path = file.getPath();
// 注意:这里替换文件分割符要用replace。因为replaceAll里面的参数是正则表达式,而windows环境中File.separator="\\"的,因此会有问题
if(path.endsWith(CLASS_SUFFIX)) {
path = path.replace(CLASS_SUFFIX, "");
// 从"/classes/"后面开始截取
String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())
.replace(File.separator, PACKAGE_SEPARATOR);
if(-1 == clazzName.indexOf("$")) {
result.add(clazzName);
}
}
return result; } else {
File[] listFiles = file.listFiles();
if(listFiles != null && listFiles.length > 0) {
for (File f : listFiles) {
if(flag) {
result.addAll(getAllClassNameByFile(f, flag));
} else {
if(f.isFile()){
String path = f.getPath();
if(path.endsWith(CLASS_SUFFIX)) {
path = path.replace(CLASS_SUFFIX, "");
// 从"/classes/"后面开始截取
String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())
.replace(File.separator, PACKAGE_SEPARATOR);
if(-1 == clazzName.indexOf("$")) {
}
}
}
}
}
}
return result;
}
} private static List<String> getAllClassNameByJar(JarFile jarFile, String packageName, boolean flag) { List<String> result = new ArrayList<>();
Enumeration<JarEntry> entries = jarFile.entries();
while(entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
String name = jarEntry.getName();
// 判断是不是class文件
if(name.endsWith(CLASS_SUFFIX)) {
name = name.replace(CLASS_SUFFIX, "").replace("/", ".");
if(flag) {
// 如果要子包的文件,那么就只要开头相同且不是内部类就ok
if(name.startsWith(packageName) && -1 == name.indexOf("$")) {
result.add(name);
}
} else {
// 如果不要子包的文件,那么就必须保证最后一个"."之前的字符串和包名一样且不是内部类
if(packageName.equals(name.substring(0, name.lastIndexOf("."))) && -1 == name.indexOf("$")) {
result.add(name);
}
}
}
}
return result;
} public static void main(String[] args) {
List<String> list = ClazzUtils.getClazzName("com.xgp.company.model", true);
for (String string : list) {
System.out.println(string);
}
}
}

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

package com.xgp.company.tool;
/**
* JDBC工具类,读取配置文件
*/ import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties; public class JDBCUtilsConfig { //定义的连接对象
private static Connection con;
//驱动
private static final String dirverClass = "com.mysql.jdbc.Driver";
//数据库的地址
private static String url;
//数据库的用户名
private static String username;
//数据库的密码
private static String password; //静态代码块读取配置文件
static {
//读取配置文件
readConfig();
try {
Class.forName(dirverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
String newUrl = url.substring(0,url.lastIndexOf('/')) + "/";
// System.out.println(newUrl);
con = DriverManager.getConnection(newUrl,username,password); //创建数据库
Statement stmt = con.createStatement(); int end = url.indexOf('&');
String data = null;
if(end == -1) {
data = url.substring(url.lastIndexOf('/') + 1);
}else {
data = url.substring(url.lastIndexOf('/') + 1,end);
} String sql = "CREATE DATABASE IF NOT EXISTS " + data +" DEFAULT CHARACTER SET = 'utf8' "; stmt.executeUpdate(sql); stmt.close();
System.out.println(data + "数据库创建成功");
//重新获取连接
con = DriverManager.getConnection(url,username,password);
} catch (SQLException e) {
e.printStackTrace();
}
} private static void readConfig() {
//使用反射技术获取文件的输入流
InputStream in = JDBCUtilsConfig.class.getClassLoader().getResourceAsStream("database.properties");
//解析properties的类
Properties pro = new Properties();
try {
//读流
pro.load(in);
} catch (IOException e) {
e.printStackTrace();
}
//获取值
username = pro.getProperty("username");
url = pro.getProperty("url");
password = pro.getProperty("password");
} //返回连接对象
public static Connection getConnection() {
return con;
} //测试是否连接数据库成功
public static void main(String[] args) {
Connection con = JDBCUtilsConfig.getConnection();
System.out.println(con);
}
}

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. re模块、正则表达式

    一.正则表达式 1.正则表达式不是Python独有的,它是一门独立的技术,所有的编程语言都可以使用正则表达式,在Python中使用正则表达式就要借助于re模块,或者是支持正则表达式书写的方法. 2.用 ...

  2. 数据表设计:多对多关系E-R图转换——中间表

    链接:https://blog.csdn.net/vainfanfan/article/details/80568784 链接2:https://www.cnblogs.com/hiwangzi/p/ ...

  3. 吴裕雄 python 机器学习——模型选择学习曲线learning_curve模型

    import numpy as np import matplotlib.pyplot as plt from sklearn.svm import LinearSVC from sklearn.da ...

  4. Redis搭建一主一从及哨兵模式(二)

    废话不多说,直接进入正题. 一.主从搭建 进入redis的根目录,拷贝一份redis.conf,最初的一份留作模版: ①cp  redis.conf redis.conf.template ②vim ...

  5. 反编译 java

    1.winrar https://www.rarlab.com/ 2.github  jd-gui http://java-decompiler.github.io/ SignNatureTest.j ...

  6. 05-Docker-Container资源限制

    目录 05-Docker-Container资源限制 参考 可压缩性 MEM限制 选项说明 压测示例 CPU限制 选项说明 压测示例 05-Docker-Container资源限制 Docker Ve ...

  7. Python - 查看类的方法和属性,dir(),help()

    1. dir()查看类的方法和属性 查看slice类的方法和属性 dir(slice) 2.help() 查看某个方法的文档 查看slice类中的indices方法 help(slice.indice ...

  8. AOP的基本认识

    一.AOP的概念 AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善. ...

  9. Spring Boot Shiro 使用教程

    Apache Shiro 已经大名鼎鼎,搞 Java 的没有不知道的,这类似于 .Net 中的身份验证 form 认证.跟 .net core 中的认证授权策略基本是一样的.当然都不知道也没有关系,因 ...

  10. Android 短信模块分析(七) MMS数据库定义及结构整理

    一. mmssms.db 数据库mmssms.db中表的定义见表4.1至4.18所示: 表4.1 addr(彩信地址) 字段名 类型 描述 备注 _id INTEGER PRIMARY_KEY 主键I ...