java jdbc 注解_java基础复习-自定义注解4(结合JDBC技术,打造类表映射微框架)...

写在前面:

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

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

1、框架的使用

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

5351d7c439a55ba88def419e1d1b2baa.png

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

868e8eb7427b5fccd33ef10f5e9ff974.png

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

05d2f891889d1cf72f1cd25b9ec8f856.png

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

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

e81fa75fef34b02515ca5d3ff6b32b6b.png

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

03ccc4764b3c28f8271b87609ca63395.png

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

a50bc56291f5a591c799709ce849f09a.png

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注解的说明:

a3e3c079be67daff738cde80dbe6cfbc.png

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

1.6、@MyField注解的说明:

85683b8f36dc1a959c4cd521f1e9aa13.png

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

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

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

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

commen为对字段的解释说明

1.6、Plan枚举类的说明:

39671d64abea4e7e7fcd89621cbf0021.png

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

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

a63fd23e7927a0a0d8cc374cac84ff91.png

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

1.8、点击运行,建库建表

2b3f3a69c985a126836c4b9b931bbc35.png

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

17968fd03900acf1f8575b67ca5ac992.png

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 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 clazz = User.class;

String tableName = getTableName(clazz);

List> filelds = null;

filelds = getFilelds(clazz);

/* for (Map 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 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> getFilelds(Class clazz){

//获取类上的字段

Field[] fields = clazz.getDeclaredFields();

List> list = new ArrayList();

for (Field field : fields) {

MyField ano = field.getAnnotation(MyField.class);

Map 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 getClazzName(String packageName, boolean showChildPackageFlag ) {

List result = new ArrayList<>();

String suffixPath = packageName.replaceAll("\\.", "/");

ClassLoader loader = Thread.currentThread().getContextClassLoader();

try {

Enumeration 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 getAllClassNameByFile(File file, boolean flag) {

List 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 getAllClassNameByJar(JarFile jarFile, String packageName, boolean flag) {

List result = new ArrayList<>();

Enumeration 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 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等框架中,是否会着更多的反射技术的使用?而这些框架给我们带来快捷开发的同时,是否也在一定程度上影响了系统的性能呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值