一.简介
Mybatis、Mybatis Plus 对数据结果集与实体类的映射关系都是通过反射技术实现,对于Mybatis Plus 的@TableField 注解映射实体也是非常有魅力。
二.实现
1.TableField.java
package com.vincent.db.utils;
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 TableField {
String name() default "";
}
2.DBUtisl.java
package com.vincent.db.utils;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;
public class DBUtils {
public static Connection getConnection(String driverCls, String url, String username, String password) throws ClassNotFoundException, SQLException {
Class.forName(driverCls);
return DriverManager.getConnection(url,username,password);
}
public static int update(Connection connection,String sql,Object ... pams) throws SQLException{
PreparedStatement statement = setStatement(connection,sql,pams);
return statement.executeUpdate();
}
private static PreparedStatement setStatement(Connection connection,String sql,Object[] pams) throws SQLException{
PreparedStatement statement = connection.prepareStatement(sql);
if(pams != null){
for(int i=1;i<=pams.length;i++){
statement.setObject(i,pams[i-1]);
}
}
return statement;
}
public static <T> List<T> select(Connection connection, String sql, Object[] pams, Class<T> entityCls) throws SQLException {
ResultSet rstSet = setStatement(connection,sql,pams).executeQuery();
//获取查询结果列名,数据库列索引从1开始
ResultSetMetaData metaData = rstSet.getMetaData();
String[] names = new String[metaData.getColumnCount()];
for(int i=1;i<=names.length;i++){
names[i-1] = metaData.getColumnLabel(i);
}
//解析cls 类字段信息
Map<String,Field> fieldMap = new HashMap<>();
for(Class cls=entityCls;cls != null;){
for(Field field : cls.getDeclaredFields()){
fieldMap.put(field.getName(),field);
}
cls = cls.getSuperclass();
}
//建立数据库列名-字段对应关系
Map<String,Field> entityMap = new HashMap<>();
for(int i=0;i<names.length;i++){
String name = names[i];
//优先解析使用注解映射的字段信息
Field field = null,nameField = null;
for(Map.Entry<String,Field> entry : fieldMap.entrySet()){
TableField tableField = entry.getValue().getAnnotation(TableField.class);
if(nameField == null && name.equals(entry.getValue().getName())){
nameField = entry.getValue();
}
if(field == null && tableField != null && name.equals(tableField.name())){
field = entry.getValue();
}
}
if(field == null && nameField != null){
field = nameField;
}
entityMap.put(name,field);
}
List<T> rst = new ArrayList<>();
while(rstSet.next()){
try {
T entity = (T)entityCls.newInstance();
for(int i=0;i<names.length;i++){
Field field = entityMap.get(names[i]);
if(field != null) {
field.setAccessible(true);
field.set(entity, rstSet.getObject(i + 1));
}
}
rst.add(entity);
}
catch (Exception e){
e.printStackTrace();
}
}
if(rstSet != null){
rstSet.close();
}
return rst;
}
public static void close(Statement statement,Connection connection){
try{
if(statement != null){
statement.close();
}
}
catch (SQLException e){
}
try{
if(connection != null){
connection.close();
}
}
catch (SQLException e){
}
}
}
3.User.java
package com.vincent.model;
import com.vincent.db.utils.TableField;
import lombok.Data;
@Data
public class User {
private Integer id;
@TableField(name = "username")
private String name;
private Integer age;
}
4.App.java
package com.vincent;
import com.vincent.db.utils.DBUtils;
import com.vincent.model.User;
import java.sql.*;
import java.util.List;
/**
* Hello world!
*
*/
public class App {
public static void main( String[] args ) throws Exception{
Connection connection = DBUtils.getConnection("com.mysql.jdbc.Driver","jdbc:mysql://localhost:3306/db_test","root","root");
for(int i=0;i<20;i++){
DBUtils.update(connection,"insert into t_user(username,age) values(?,?)","name"+i,i);
}
List<User> users = DBUtils.select(connection,"select * from t_user",null, User.class);
System.out.println(users);
}
}
效果:
三.总结
1.反射是Java 开发利器,尤其对于框架开发
2.注解+反射使得繁琐的配置文件编写不在是噩梦
3.注解+反射 对于权限检查、方法参数注入都是非常实用,如springboot HandlerInterceptor 自定义需要拦截的mapping,HandlerMethodArgumentResolver 给mapping 方法添加注解参数实体