一直没什么机会使用自定义注解,今天现学现卖尝试写了个小demo,定义了一个属性注解用于标注在实体类的属性上,一个处理类用于获取从指定数据库查询出对应的结果集。主要是想实现自定义注解的功能思路所以代码中基本没考虑非注解相关各种情况的处理。
注解类MyField.java
package com.test;
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 MyField {
//当实体类的属性名于表中的列名不同时,接收列名作为参数
String columnName() default "";
}
执行查询操作的工具类QueryUtil.java
package com.test;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class QueryUtil {
/**
* 从数据库中查询指定类的所有结果
*
* @param clazz
* @param <T>
* @return
*/
public static <T> List<T> selectAll(Class<T> clazz) {
//拼接sql
StringBuilder sql = new StringBuilder("select ");
//使用反射获取指定类的所有属性
Field[] fields = clazz.getDeclaredFields();
//定义一个属性和列名关系的map,一会儿遍历结果集的时候要用
Map<Field, String> fieldColumnMap = new HashMap<Field, String>();
//遍历所有属性
for (Field field : fields) {
//判断属性是否被我们指定的MyField注解所标识
if (field.isAnnotationPresent(MyField.class)) {
//取得当前属性上对应的注解对象
MyField myField = field.getAnnotation(MyField.class);
//如果注解指定了列名则用指定的列名,否则直接用属性名
String columnName = "";
if (!"".equals(myField.columnName())) {
columnName = myField.columnName();
} else {
columnName = field.getName();
}
sql.append(columnName + ", ");
//将有注解标注的属性和对应的列名放进他们对应关系的map
fieldColumnMap.put(field, columnName);
}
}
//继续拼接sql
sql.replace(sql.lastIndexOf(","), sql.length() - 1, " from ");
sql.append(clazz.getSimpleName().toLowerCase());
//基础的jdbc操作
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
List<T> list = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
ps = conn.prepareStatement(sql.toString());
rs = ps.executeQuery();
list = new ArrayList<T>();
//遍历结果集
while (rs.next()) {
T t = clazz.newInstance();
//遍历刚才定义的map,通过列名取出结果,通过反射获取对应属性的set方法,组装实体类对象
for (Map.Entry<Field, String> entry : fieldColumnMap.entrySet()) {
Object columnResult = rs.getObject(entry.getValue());
Field field = entry.getKey();
String fieldName = field.getName();
String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
clazz.getDeclaredMethod(methodName, field.getType()).invoke(t, columnResult);
}
//组装完成,加入方法返回的list
list.add(t);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return list;
}
}
待查询的对象Book.java
package com.test;
/**
* 在实体类中,给id设置了默认的@MyField注解,给那nameaaa设置了带参的注解用于注明属性名于列名不同,而author没设注解
*/
public class Book {
@MyField
private Integer id;
@MyField(columnName = "name")
private String nameaaa;
private String author;
public Book() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", nameaaa='" + nameaaa + '\'' +
", author='" + author + '\'' +
'}';
}
public String getNameaaa() {
return nameaaa;
}
public void setNameaaa(String nameaaa) {
this.nameaaa = nameaaa;
}
}
测试类Test.java
package com.test;
import java.util.List;
public class Test {
public static void main(String[] args) throws Exception {
List<Book> books = QueryUtil.selectAll(Book.class);
System.out.println(books);
}
}
执行结果:
[Book{id=1, nameaaa='红楼梦1', author='null'}, Book{id=2, nameaaa='红楼梦2', author='null'}, Book{id=3, nameaaa='红楼梦3', author='null'}, Book{id=4, nameaaa='红楼梦4', author='null'}, Book{id=5, nameaaa='红楼梦', author='null'}, Book{id=7, nameaaa='红楼梦', author='null'}, Book{id=8, nameaaa='红楼梦', author='null'}, Book{id=9, nameaaa='红楼梦', author='null'}, Book{id=10, nameaaa='红楼梦', author='null'}, Book{id=11, nameaaa='红楼梦', author='null'}, Book{id=12, nameaaa='红楼梦', author='null'}, Book{id=15, nameaaa='红楼梦', author='null'}, Book{id=16, nameaaa='红楼梦', author='null'}, Book{id=17, nameaaa='红楼梦', author='null'}, Book{id=18, nameaaa='红楼梦', author='null'}, Book{id=19, nameaaa='红楼梦', author='null'}, Book{id=20, nameaaa='红楼梦', author='null'}, Book{id=21, nameaaa='红楼梦', author='null'}, Book{id=22, nameaaa='红楼梦', author='null'}, Book{id=23, nameaaa='红楼梦', author='null'}, Book{id=24, nameaaa='红楼梦', author='null'}, Book{id=25, nameaaa='红楼梦', author='null'}, Book{id=26, nameaaa='红楼梦', author='null'}]
Process finished with exit code 0
可以看到,在实体类中我们给id设置了默认的@MyField注解,给nameaaa设置了带参的注解用于注明属性名于列名不同,而author没设注解,所以id和name都查出了结果而author虽然在数据库中有值但返回null,查询结果符合预期。
按照这个思路,如果想要实现增删改查只需要在工具类中新增对应的方法,接收对应的参数并完善各种可能发生的情况的处理措施就行了。