概述
上一篇我们用apt
,实现了一个自动生成工厂类的实例。这篇我们使用反射的方式去实现一个持久层框架,当然只是核心代码,并不是可以直接拿来使用的商业项目 ,类似于 Hibernate
。
自定义注解系列文章
- 那些高端、优雅的注解是怎么实现的<0> -- 注解的分类
- 那些高端、优雅的注解是怎么实现的 <1> -- 自定义注解语法
- 那些高端、优雅的注解是怎么实现的<2> -- 解析注解
- 那些高端、优雅的注解是怎么实现的<3> - 可继承性@Inherited
- 那些高端、优雅的注解是怎么实现的<4> -- 使用Annotaion Processing Tool 解析注解
- 那些高端、优雅的注解是怎么实现的<5> --使用Annotaion Processing Tool 自定义注解
- 那些高端、优雅的注解是怎么实现的<6> --自定义持久层框架-类 Hibernate
需求
- 有一张用户表(user),包括用户id、用户名、昵称、年龄、性别、邮箱、手机号等字段
- 根据
bean
类中包含的信息查询在 user 表中查询出符合条件的 user
定义实体类并和 user 表形成映射关系
定义bean
类 Customer
@Table("user")
public class Customer {
//id
@Column("id")
private int id;
//姓名
@Column("user_name")
private String userName;
//年龄
@Column("age")
private int age;
//城市
@Column("city")
private String city;
//邮箱
@Column("email")
private String email;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
我们需要定义@Table
注解来让 user
表和 Customer
类之间形成映射关系。需要定义 @Column
让 user
表 和 Customer
类的字段之间形成映射关系。
定义注解
- 定义
@Table
注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Table {
String value();
}
- 定义
@Column
注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Column {
String value();
}
解析注解并生成相应的逻辑代码
我们最后需要生成sql语句类似如下
select * from user where 1=1 and user_name='zhangsan' and age=18 and email in('xxx@163.com,xxx@qq.com,xxx@yahoo.com.cn')
下面我们定义query 方法,去解析注解,并拼接完成我们的 sql 语句 。
public static String query(Object bean) {
//因为最后我们会返回sql语句,所以这里我们需要拼接sql语句
StringBuilder sb = new StringBuilder();
//首先获取 bean 类的 class,本例就是 Customer
Class<?> beanClass = bean.getClass();
//判断该 class 上是否有 table 注解
boolean present = beanClass.isAnnotationPresent(Table.class);
if (!present) {
return "";
}
//获取该 class上 table 注解
Table table = beanClass.getAnnotation(Table.class);
//获取表名
String tableName = table.value();
sb.append("select * from")
.append(" ")
.append(tableName)
.append(" ")
//防止没有查询条件而报错
.append("where 1=1");
//获取类里面的所有字段
Field[] fields = beanClass.getDeclaredFields();
//遍历所有字段
for (Field field : fields) {
//判断字段上是否有Colomn 注解
boolean fieldPresent = field.isAnnotationPresent(Column.class);
//如果不存在,跳过这个字段继续下一个循环
if (!fieldPresent) {
continue;
}
//获取到 Column
Column column = field.getAnnotation(Column.class);
//获取 Column 的名称,如(user_name)
String columnName = column.value();
//获取字段的名称,如 useName
String fieldName = field.getName();
//通过该字段的get方法名
String fieldGetName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Object value = null;
try {
//获取到get方法
Method method = beanClass.getMethod(fieldGetName);
//反射调用该字段的get方法,获取到该字段的值
value = method.invoke(bean);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
//如果不为空,则开始添加 and 语句
if (value == null ||((value instanceof Integer)&& (Integer)value==0)) {
continue;
}
//如果有值则添加 and 语句
sb.append(" ")
.append("and").append(" ")
.append(columnName)
;
//如果这个字段是int类型
if (value instanceof Integer) {
sb.append("=").append(value);
} else if (value instanceof String) {
//如果包含逗号,则是in查询
if (((String) value).contains(",")) {
String[] values = ((String) value).split(",");
//添加 in 语句
sb.append(" ").append("in('");
//添加查询条件,并用逗号隔开
for (String item : values) {
sb.append(item).append(",");
}
//去除最后一个逗号
sb.deleteCharAt(sb.length() - 1);
sb.append("')");
}
//如果单个查询用 = 即可,但需要加入单引号
else {
sb.append("='").append(value).append("'");
}
}
}
return sb.toString();
}
调用 query 方法,获取sql语句
public static void main(String[] args) {
// new Customer,并赋值
Customer customer1 = new Customer();
customer1.setUserName("zhangsan");
customer1.setAge(18);
customer1.setEmail("xxx@163.com,xxx@qq.com,xxx@yahoo.com.cn");
//调用 query 方法,获取 sql 语句
String sql1 = Query.query(customer1);
//打印sql语句
System.out.println(sql1);
}
总结
完整的代码我会上传到 github,如果仅仅上上面的代码片段无法看懂,那clone下来看看吧!
网友评论