反射——CRUD小工具
用反射封装了一个用于增删改查的工具类。现在市面上已经有很多成熟的持久层框架,所以这里只是为了熟悉反射机制,且了解一点市面上持久层框架最基础的原理。写的这个工具类有一些限制,比如实体类必须与表名一致,主键名字必须是id,实体类名字与数据库表名必须一致(如UserEntity – userEntity),实体类所有的字段和表的字段需要一致,究其原因是因为没有自己的专门用于关系映射的配置文件或者注解,想要解决关系映射也简单,将映射关系写进一个配置文件或者自己做注解开发做映射。
1、工具类
package com.xsl.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* 一个Crud的小工具类,可以单表增删改查任何实体类
* 前提:表名与类名一致,表名开头小写,类名开头大写,类字段与表字段一致,表字段没有多余的无用字段
* @param <T>实体类
* @param <K>主键
* @author xsl
*/
public class CrudUtil<T, K> {
private static String DRIVER = "com.mysql.jdbc.Driver";
private static String URL = "jdbc:mysql://192.168.44.131:3306/crud?useUnicode=true&characterEncoding=UTF-8";
private static String USERNAME = "root";
private static String PASSWORD = "root";
//注册驱动
static {
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private Connection getConnection() throws SQLException {
Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
return connection;
}
/**
* 查找数据库中所有此实体类对应的数据封装实体对象
*
* @param cls 实体类对应的字节码对象
* @return 所有的实体类
* @throws Exception
*/
public List<T> findAll(Class cls) throws Exception {
String tableName = getTableName(cls);
String sql = "select * from " + tableName;
Connection connection = getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();
List<T> list = new ArrayList<>();
while (resultSet.next()) {
list.add(resultTransferEntity(resultSet, cls));
}
connection.close();
return list;
}
/**
* 根据id查找该实体类对象的对应数据封装
*
* @param cls 实体类字节码
* @param id 要查找的id
* @return 实体类对象
* @throws Exception
*/
public T findById(Class cls, K id) throws Exception {
String tableName = getTableName(cls);
String sql = "select * from " + tableName + " where id = ?";
Connection connection = getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatementSetValue(preparedStatement, id, 1);
ResultSet resultSet = preparedStatement.executeQuery();
T t = null;
while (resultSet.next()) {
t = resultTransferEntity(resultSet, cls);
}
return t;
}
/**
* 根据id删除
*
* @param cls 要删除的对象的字节码对象
* @param id 要删除的对象的id
* @throws Exception
*/
public void deleteById(Class cls, K id) throws Exception {
String tableName = getTableName(cls);
String sql = "delete from " + tableName + " where id = ?";
Connection connection = getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatementSetValue(preparedStatement, id, 1);
preparedStatement.executeUpdate();
}
/**
* 将实体类持久化到数据库
* @param t 实体类对象
* @throws Exception
*/
public void save(T t) throws Exception {
Class<?> cls = t.getClass();
String tableName = getTableName(cls);
StringBuilder firstSql = new StringBuilder(" insert into " + getTableName(cls) + " ( ");
StringBuilder lastSql = new StringBuilder(" values ( ");
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (!isNull(field, t)) {
firstSql.append(field.getName()).append(" ,");
lastSql.append(" ? ,");
}
}
firstSql.replace(firstSql.length() - 1, firstSql.length(), " ").append(" ) ");
lastSql.replace(lastSql.length() - 1, lastSql.length(), " ").append(" ) ");
Connection connection = getConnection();
String sql = new String(firstSql.append(lastSql));
System.out.println(sql);
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < fields.length; i++) {
if (!isNull(fields[i], t)) {
preparedStatementSetValue(preparedStatement, fields[i].get(t), i + 1);
}
}
preparedStatement.executeUpdate();
}
/**
* 修改对象,该对象必须要id
* @param t
*/
public void update(T t) throws Exception {
Class<?> cls = t.getClass();
StringBuilder firstSql = new StringBuilder(" update ").append(getTableName(t.getClass())).append(" set ");
StringBuilder lastSql = new StringBuilder(" where id = ? ");
Field[] declaredFields = cls.getDeclaredFields();
int index = 0;
for (Field field : declaredFields) {
field.setAccessible(true);
if (!isNull(field,t)){
firstSql.append(field.getName()).append(" = ?,");
index++;
}
}
firstSql.replace(firstSql.length() - 1, firstSql.length(), " ");
StringBuilder sql = firstSql.append(lastSql);
System.out.println(new String(sql));
Connection connection = getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(new String(sql));
for (Field filed : declaredFields) {
filed.setAccessible(true);
preparedStatementSetValue(preparedStatement,filed.get(t),index);
}
for (int i = 0; i < declaredFields.length ; i++) {
if (!isNull(declaredFields[i], t)) {
preparedStatementSetValue(preparedStatement, declaredFields[i].get(t), i + 1);
}
}
Field id = cls.getDeclaredField("id");
id.setAccessible(true);
preparedStatementSetValue(preparedStatement,id.get(t),index+1);
preparedStatement.executeUpdate();
}
/**
* @param preparedStatement 由Connection预编译sql语句得到的preparedStatement
* @param value 给preparedStatement的设置的值
* @param index preparedStatement的索引
* @throws SQLException
*/
private void preparedStatementSetValue(PreparedStatement preparedStatement, Object value, int index) throws SQLException {
switch (value.getClass().getName()) {
case "int":
case "java.lang.Integer":
preparedStatement.setInt(index, (Integer) value);
break;
case "java.lang.String":
preparedStatement.setString(index, (String) value);
break;
case "long":
case "java.lang.Long":
preparedStatement.setLong(index, (Long) value);
break;
default:
throw new RuntimeException("id别乱用类型了别闹");
}
}
/**
* @param cls 实体类的字节码对象
* @return 获取这个实体类对应的表名
*/
private String getTableName(Class cls) {
String name = cls.getName();
String[] split = name.split("\\.");
String str = split[split.length - 1];
String table = str.substring(0,1).toLowerCase()+str.substring(1);
return table;
}
private boolean isNull(Field field, T t) throws IllegalAccessException {
boolean flag = false;
if (field.get(t) == null) {
flag = true;
}
return flag;
}
private T resultTransferEntity(ResultSet resultSet, Class cls) throws Exception {
Constructor<T> constructor = cls.getConstructor();
T t = constructor.newInstance();
Field[] declaredFields = cls.getDeclaredFields();
for (Field filed : declaredFields) {
filed.setAccessible(true);
Class<?> type = filed.getType();
String filedName = filed.getName();
switch (type.getName()) {
case "int":
case "java.lang.Integer":
filed.set(t, resultSet.getInt(filedName));
break;
case "java.lang.String":
filed.set(t, resultSet.getString(filedName));
break;
case "long":
case "java.lang.Long":
filed.set(t, resultSet.getLong(filedName));
break;
case "short":
case "java.lang.Short":
filed.set(t, resultSet.getShort(filedName));
break;
case "byte":
case "java.lang.Byte":
filed.set(t, resultSet.getByte(filedName));
break;
case "float":
case "java.lang.Float":
filed.set(t, resultSet.getFloat(filedName));
break;
case "double":
case "java.lang.Double":
filed.set(t, resultSet.getDouble(filedName));
break;
default:
throw new RuntimeException("功能有限敬请原谅");
}
}
return t;
}
}
2、使用案例
2.1 User实体类
package com.xsl.entity;
public class User {
private int id;
private String name;
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(int id, String name) {
this.id = id;
this.name = name;
}
public User() {
}
}
2.2测试使用demo
package com.xsl.test;
import com.xsl.entity.User;
import com.xsl.util.CrudUtil;
import org.junit.Test;
import java.util.List;
public class CrudTest {
CrudUtil<User,Integer> util = new CrudUtil<>();
@Test
public void testFindAll() throws Exception {
List<User> all = util.findAll(User.class);
System.out.println(all);
}
@Test
public void testFindById() throws Exception {
User user = util.findById(User.class, 1);
System.out.println(user);
}
@Test
public void testDeleteById() throws Exception {
util.deleteById(User.class,1);
}
@Test
public void testSave() throws Exception {
User user = new User();
user.setId(4);
util.save(user);
}
@Test
public void testUpdate() throws Exception {
User user = new User(2,"wjl");
util.update(user);
}
}