使用反射+自定义注解实现自动生成简易的查询SQL语句
自定义注解:
@Target(ElementType.TYPE) //用于注释类或接口
@Retention(RetentionPolicy.RUNTIME) //运行时注解
@Inherited //允许被继承使用
@Documented
public @interface Table {
/**
* 数据库表名
* @return java.lang.String
* @author zhj
* @date 2019/4/1
*/
String value();
}
@Target(ElementType.FIELD) //用于字段注解
@Retention(RetentionPolicy.RUNTIME) //运行时注解
@Inherited
@Documented
public @interface Column {
/**
* 字段的名称
* @return java.lang.String
* @author zhj
* @date 2019/4/1
*/
String value();
}
↑自定义注解:@Table 和@Column
实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Table("user")
public class User {
@Column("user_name")
private String name;
@Column("user_password")
private String password;
@Column("user_id")
private Integer id;
@Column("user_address")
private String address;
}
↑实体类使用了自定义注解标注属性和类
工具类:
public class GenerateSQLUtil {
private static GenerateSQLUtil gu = new GenerateSQLUtil();
private GenerateSQLUtil(){}
public static GenerateSQLUtil getInstance() {
return gu;
}
/**
* <b>返回使用@Table注解的对象的查询sql语句</b><br/>
* 使用方法:<br/>
* 1.传入的Object类必须使用@Table注解<br/>
* 2.Object类需要查询的字段应用@Column(表字段名)注解<br/>
* 3.Object类需要有Getter方法<br/>
* @param table 使用@Table注解的对象
* @return java.lang.String
* @author zhj
* @creed: Talk is cheap,show me the code
* @date 2019/4/1
*/
public String query(Object table) throws Exception {
//初始化SQL语句
StringBuilder sb = new StringBuilder("select * form ");
//通过反射,拿到class
Class tab = table.getClass();
//判断class是否有@Table注解
boolean tExists = tab.isAnnotationPresent(Table.class);
if (!tExists) {
//没有注解,则抛出异常
throw new Exception("该类没有使用相关注解");
}
//有@Table注解, 则通过注解拿到表名
String tableName = ((Table)tab.getAnnotation(Table.class)).value();
//拼接SQL语句
sb.append(tableName).append(" where 1=1 ");
appendColumnSql(table, sb, tab);
return sb.toString();
}
private void appendColumnSql(Object table,StringBuilder sb, Class tab) {
//取类的属性
Field[] fields = tab.getDeclaredFields();
//遍历属性是否有注解
for (Field field : fields) {
if (!field.isAnnotationPresent(Column.class)) {
//该字段没有@Column注解,则continue
continue;
}
Column fAnno = field.getAnnotation(Column.class);
//拿到注解的value值,即表字段名
String columnName = fAnno.value();
//类属性名
String fieldName = field.getName();
//判断对象中该属性是否有值,有值则默认此为查询条件之一
try {
String methodName = new StringBuffer("get").append(fieldName.substring(0,1).toUpperCase())
.append(fieldName.substring(1)).toString();
Method getterMethod
= tab.getMethod(methodName);
System.out.println("methodName:" + methodName);
//调用getter方法拿到对象参数
Object fieldValue = getterMethod.invoke(table);
//拼接SQL
//=null说明对象没有该参数,无需拼接
if (fieldValue != null) {
sb.append(" and ").append(columnName);
//如果是String,并且包含“,”说明是多参数,应该拼接in
if (fieldValue.toString().contains(",") && fieldValue instanceof String) {
sb.append(" in (");
//切割参数
String[] propertys =((String) fieldValue).split(",");
for (int i = 0; i < propertys.length;) {
sb.append("`").append(propertys[i]).append("`");
if (++i == propertys.length) {
sb.append(")");
break;
}
sb.append(",");
}
//如果是String类型,则需要增加``符号
} else if (fieldValue instanceof String) {
sb.append(" =`")
.append(fieldValue).append("`");
//其他情况,可以扩展
} else {
sb.append(" =").append(fieldValue);
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
测试类:
public class GenerateSQLTest {
public static void main(String[] args) {
User u = new User();
u.setAddress("厦门,泉州,福州");
u.setId(55);
try {
String sql = GenerateSQLUtil.getInstance().query(u);
System.out.println("sql语句为:" + sql);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
methodName:getName
methodName:getPassword
methodName:getId
methodName:getAddress
sql语句为:select * form user where 1=1 and
user_id =55 and user_address in (`厦门`,`泉州`,`福州`)
实现的原理:通过反射判断类中是否有使用自定义注解,有的话,则拿取注解的value以及拿取对象的属性值进行拼接sql语句,该demo只是做了简单的query语句,可以通过复杂的条件判断实现更复杂的sql语句生成。
1.反射
获取所有属性:Field[] getDeclaredFields();
获取所有方法:Method[] getMethods();
获取对象的属性值:Object invoke(Object object)
获取类对象:Class Class.forName(相对路径包className);
2.自定义注解:
@Target(ElementType.FIELD) //用于字段注解
@Retention(RetentionPolicy.RUNTIME) //运行时注解
@Inherited //子类是否能继承
@Documented //文档