本实例主要是学习java反射和JDBC的相关内容。本实例涉及到的内容没有用完并且代码健壮性比较差,可扩展性还有很多。该实例适用于maven/gradle项目,并且使用mysql8.0。普通项目记得修改扫描目录。
1. 定义四个注解类
(1)AutoWired作用于属性,标记注入属性
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoWired {
}
(2)DaoResource作用于接口,表示这是一个jdbc操作接口
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DaoResource {
}
(3) Field作用于属性,表示这是一个数据库字段
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Field {
}
(4)Table 作用于类,表示这是一个实体类,对应数据库表
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
}
2. 定义BaseDao接口,模拟Mybatis plus的Mapper接口
import java.sql.SQLException;
public interface BaseDao<T>
{
public void create(T entity) throws SQLException;
public void deleteById(Long id) throws SQLException;
public void insert(T entity) throws SQLException;
public T selectById(Long id) throws SQLException;
public void update(T entity) throws SQLException;
}
3. 定义JdbcUtils类,对数据库的基本操作。
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
// 不适用并发
public class JdbcUtils
{
private String url = "jdbc:mysql://localhost:3306/test?characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false";
private String user = "root";
private String password = "root";
static
{
try
{
String driver = "com.mysql.cj.jdbc.Driver";
Class.forName(driver);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
public synchronized Connection getConnection() throws SQLException
{
Connection connection = DriverManager.getConnection(url, user, password);
connection.setAutoCommit(false);
return connection;
}
public void deleteById(Class<?> entityClass, Long id) throws SQLException
{
String tableName = entityClass.getSimpleName();
String sql = "delete from " + tableName + " where id=" + id;
System.out.println(sql);
execute(sql);
}
public void insert(Object entity) throws SQLException
{
StringBuilder sql = new StringBuilder();
StringBuilder sqlFields = new StringBuilder();
StringBuilder sqlValues = new StringBuilder();
Class<?> clazz = entity.getClass();
String tableName = clazz.getSimpleName();
Field[] fields = clazz.getDeclaredFields();
sql.append("insert into ").append(tableName);
sqlFields.append(" (");
sqlValues.append(" (");
for (Field field : fields)
{
try
{
field.setAccessible(true);
Object value = field.get(entity);
String type = field.getGenericType().getTypeName();
if (type == "id" && value == null)
{
throw new RuntimeException("id 不能为空");
}
switch (type)
{
case "java.lang.String":
sqlFields.append(field.getName());
sqlValues.append(value == null ? null : "'" + value + "'");
break;
case "java.lang.Integer":
sqlFields.append(field.getName());
sqlValues.append(value);
break;
default:
break;
}
sqlFields.append(",");
sqlValues.append(",");
}
catch (IllegalArgumentException | IllegalAccessException e)
{
e.printStackTrace();
}
}
System.out.println(sqlFields.toString());
System.out.println(sqlValues.toString());
sqlFields.replace(sqlFields.lastIndexOf(","), sqlFields.length(), ") ");
sqlValues.replace(sqlValues.lastIndexOf(","), sqlValues.length(), ") ");
sql.append(sqlFields).append(" value ").append(sqlValues);
System.out.println(sql);
execute(sql.toString());
}
@SuppressWarnings("deprecation")
public Object selectById(Class<?> entityClass, Long id) throws SQLException
{
String tableName = entityClass.getSimpleName();
String sql = "select * from " + tableName + " where id=" + id;
System.out.println(sql);
{
try (Connection connection = getConnection(); Statement statement = connection.createStatement();)
{
ResultSet rs = statement.executeQuery(sql.toUpperCase());
if (rs.next())
{
Field[] fields = entityClass.getDeclaredFields();
Object result = entityClass.newInstance();
for (Field field : fields)
{
field.setAccessible(true);
Object value = rs.getObject(field.getName());
field.set(result, value);
}
return result;
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
return null;
}
public void update(Object entity) throws SQLException
{
StringBuilder sql = new StringBuilder();
Class<?> clazz = entity.getClass();
String tableName = clazz.getSimpleName();
Field[] fields = clazz.getDeclaredFields();
sql.append("update ").append(tableName).append(" set ");
for (Field field : fields)
{
try
{
field.setAccessible(true);
Object value = field.get(entity);
String type = field.getGenericType().getTypeName();
if (field.getName().toLowerCase() != "id" && field.get(entity) != null)
{
switch (type)
{
case "java.lang.String":
sql.append(field.getName() + "='" + value + "'");
break;
case "java.lang.Integer":
sql.append(field.getName() + "=" + value);
break;
default:
break;
}
sql.append(",");
}
}
catch (IllegalArgumentException | IllegalAccessException e)
{
e.printStackTrace();
}
}
try
{
Field field = clazz.getDeclaredField("id");
field.setAccessible(true);
Object id = field.get(entity);
sql.delete(sql.length() - 1, sql.length());
sql.append(" where id=" + id);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println(sql.toString());
execute(sql.toString());
}
public void createTable(Object entity) throws SQLException
{
StringBuilder sql = new StringBuilder();
Class<?> clazz = entity.getClass();
String tableName = clazz.getSimpleName();
Field[] fields = clazz.getDeclaredFields();
sql.append("CREATE TABLE ");
sql.append(tableName);
sql.append("( ");
for (Field field : fields)
{
field.setAccessible(true);
try
{
String fieldName = field.getName();
String type = field.getGenericType().getTypeName();
switch (type)
{
case "java.lang.String":
sql.append(fieldName + " VARCHAR(100),");
break;
case "java.lang.Integer":
sql.append(fieldName + " int ,");
break;
default:
break;
}
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
}
sql.delete(sql.length() - 1, sql.length());
sql.append(" ) DEFAULT CHARSET=utf8;");
String sqlStr = sql.toString().toUpperCase();
if (!sqlStr.contains("EXISTS"))
{
if (!tableExists(tableName))
{
execute(sqlStr);
}
}
}
private void execute(String sql)
{
try (Connection connection = getConnection(); Statement statement = connection.createStatement();)
{
statement.execute(sql.toUpperCase());
connection.commit();
}
catch (Exception e)
{
e.printStackTrace();
}
}
private Boolean tableExists(String tableName) throws SQLException
{
int numRows = 0;
try (Connection connection = getConnection();
ResultSet resultSet = connection.getMetaData().getTables(null, null, tableName.toUpperCase(), null);)
{
while (resultSet.next())
++numRows;
}
catch (Exception e)
{
e.printStackTrace();
}
return numRows > 0;
}
public void close(Connection connection, Statement statement, ResultSet resultSet)
{
try
{
if (connection != null)
{
connection.close();
}
if (statement != null)
{
statement.close();
}
if (resultSet != null)
{
resultSet.close();
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
4. 定义Person类对上述工具类可以做相关测试
@Table
public class Person
{
@Field
private Integer id;
@Field
private String name;
@Field
private Integer age;
public Person()
{
}
public Person(Integer id, String name, Integer age)
{
super();
this.id = id;
this.name = name;
this.age = age;
}
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public Integer getAge()
{
return age;
}
public void setAge(Integer age)
{
this.age = age;
}
@Override
public String toString()
{
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
5. 定义PersonDao接口并继承BaseDao,这里模仿MyBatis plus的使用方法。
import java.sql.SQLException;
@DaoResource
public interface PersonDao extends BaseDao<Person>
{
public void deleteByName(Long id) throws SQLException;
}
6. Application类是对整个应用进行控制的核心类。
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Application
{
// system
private String packagePath = System.getProperty("user.dir") + File.separator + "src" + File.separator
+ "main" + File.separator + "java" + File.separator;
private Map<Class<?>, Object> instancedClasses = new HashMap<>();
private List<Class<?>> classes = new ArrayList<>();
private List<Class<?>> baseDaoClasses = new ArrayList<>();
private List<Class<?>> entityClasses = new ArrayList<>();
private static Application application = new Application();
@AutoWired
private JdbcUtils jdbcUtils;
public static Application run()
{
try
{
application.init();
}
catch (Exception e)
{
e.printStackTrace();
}
return application;
}
// 反射操作
public void init() throws Exception
{
instancedClasses.put(this.getClass(), application);
instancedClasses.put(JdbcUtils.class, new JdbcUtils());
String basePackage = "com._jdbc_03";
File file = new File(packagePath + basePackage.replace(".", File.separator));
loadClasses(file);
parserBaseDaoClasses();
parserAutoWiredFields();
}
@SuppressWarnings("deprecation")
private void parserAutoWiredFields()
throws IllegalArgumentException, IllegalAccessException, InstantiationException, ClassNotFoundException
{
for (Class<?> clazz : classes)
{
Object result = instancedClasses.get(clazz);
if (result == null && !clazz.isInterface())
{
result = clazz.newInstance();
instancedClasses.put(clazz, result);
}
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields)
{
field.setAccessible(true);
if (field.isAnnotationPresent(AutoWired.class))
{
System.out.println(field.getType());
Object object = instancedClasses.get(field.getType());
System.out.println(object);
if (object != null)
{
System.out.println("获取不为空");
field.set(result, object);
}
else
{
//field.set(object,field.getType().newInstance());
System.out.println("获取为空");
}
}
}
}
}
// https://www.jb51.net/article/124178.htm
private void parserBaseDaoClasses()
{
for (Class<?> clazz : baseDaoClasses)
{
Object object = Proxy
.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
if (Object.class.equals(method.getDeclaringClass()))
{
return method.invoke(this, args);
}
else
{
String methodName = method.getName();
switch (methodName)
{
case "create":
jdbcUtils.createTable(args[0]);
break;
case "insert":
jdbcUtils.insert(args[0]);
break;
case "selectById":
{
Type[] genericInterfaces = clazz.getGenericInterfaces();
for (Type genericInterface : genericInterfaces)
{
Type rawType = ((ParameterizedType) genericInterface).getRawType();
Class<?> interfaceType = (Class<?>) rawType;
if (interfaceType == BaseDao.class)
{
Type[] types = ((ParameterizedType) genericInterface).getActualTypeArguments();
Class<?> entityClass = (Class<?>) types[0];
return jdbcUtils.selectById(entityClass, (Long) args[0]);
}
}
}
break;
case "update":
jdbcUtils.update(args[0]);
break;
case "deleteById":
{
Type[] genericInterfaces = clazz.getGenericInterfaces();
for (Type genericInterface : genericInterfaces)
{
Type rawType = ((ParameterizedType) genericInterface).getRawType();
Class<?> interfaceType = (Class<?>) rawType;
if (interfaceType == BaseDao.class)
{
Type[] types = ((ParameterizedType) genericInterface).getActualTypeArguments();
Class<?> entityClass = (Class<?>) types[0];
jdbcUtils.deleteById(entityClass, (Long) args[0]);
}
}
}
break;
default:
// other
break;
}
}
return null;
}
});
instancedClasses.put(clazz, object);
}
}
public Object getClass(Class<?> clazz)
{
return instancedClasses.get(clazz);
}
// 加载所有类
private void loadClasses(File file) throws ClassNotFoundException
{
if (file.exists())
{
File[] files = file.listFiles();
if (null != files && files.length > 0)
{
for (File f : files)
{
if (f.isFile() && f.getName().endsWith(".java"))
{
String filePath = f.getAbsolutePath();
String qualifiedName = filePath
.substring(packagePath.length(), filePath.length() - 5)
.replace(File.separator, ".");
Class<?> clazz = Class.forName(qualifiedName);
classes.add(clazz);
if (!clazz.isAnnotation())
{
Class<?>[] clazzInterfaces = clazz.getInterfaces();
if (Arrays.asList(clazzInterfaces).contains(BaseDao.class))
{
baseDaoClasses.add(clazz);
}
if (clazz.isAnnotationPresent(Table.class))
{
entityClasses.add(clazz);
}
}
}
else if (f.isDirectory())
{
loadClasses(f);
}
}
}
}
}
}
7. Main类是该实例的主类,使用方法模仿springboot
import java.sql.SQLException;
public class Main
{
@AutoWired
private PersonDao personDao;
public void personDaoTest() throws SQLException
{
personDao.create(new Person());
personDao.insert(new Person(2, "小明", 18));
System.out.println("插入成功!");
Person person = personDao.selectById(2L);
System.out.println("查询到的信息:" + person);
personDao.update(new Person(2, null, 28));
System.out.println("更新成功!");
personDao.selectById(2L);
System.out.println("更新后的值:" + person);
personDao.deleteById(2L);
System.out.println("删除成功!");
}
public static void main(String[] args) throws SQLException
{
Application application = Application.run();
Main main = (Main) application.getClass(Main.class);
main.personDaoTest();
}
}