apt与访问者结合.

     apt 其实有更加优雅的实现方案, oracle 对 apt 提供了对访问者的支持.

     如果没有接触过 apt 的小伙伴可先看一下上一篇文章. apt 使用基础, 如果对于访问者模式不太清除的话, 可以先问一下 google 霸霸, 这里顺便推荐两本设计模式的书籍, 一本是 << 大化设计模式 >>, 还有一本是 <<设计模式之禅 >>, 有兴趣的小伙伴可以关注下.

     访问者模式适用于数据结构不会发生变化, 而算法却会变化的场景. apt 是作用在一个类上的, 而类相关的信息, 方法, 域, 内部类, 注解, 这些结构基本是不会变化的, 所以非常符合访问者模式的是用场景.
     当然, 我们不需要去自己写一个访问者的结构. Oracle 的大神们都给我们封装好了, 我们只需要添加不同的 Visitor 就可以了. 还是看 <<Think in Java >> 中的例子, 不过我们使用新版本的 api.

     具体的操作相信通过上一篇博客, 应该已经轻车熟路了, 我们先按照上一篇文章的流程, 建立 annotation Model 等准备工作, 然后定义的注解. NOTE: 以下几个类需要放在 annotation Model 下.

1
2
3
4
5
6
7
8
9
10
11
12
// omit package and import
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
boolean primaryKey() default false;
boolean allowNull() default true;
boolean unique() default false;
}
1
2
3
4
5
6
7
// omit package and import
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
String name() default "";
}
1
2
3
4
5
6
7
8
9
// omit package and import
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
String name() default "";
Constraints constraints() default @Constraints;
}
1
2
3
4
5
6
7
8
9
10
11
// omit package and import
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
int value() default 0;
String name() default "";
Constraints constraints() default @Constraints;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// omit package and import
// 这个注解解释器的代码略长, 不过已经分成了几个模块
// 指明支持的注解. 也可以通过重写 AbstractProcessor 的方法实现.
@SupportedAnnotationTypes(value = {
"com.congspark.annotation.database.Constraints",
"com.congspark.annotation.database.DBTable",
"com.congspark.annotation.database.SQLInteger",
"com.congspark.annotation.database.SQLString"
})
public class TableCreateProcessor extends AbstractProcessor {
private StringBuilder sqlBuilder = new StringBuilder();
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
// 获取 DBTable 注解标记的类的集合.
for (Element element : roundEnvironment.getElementsAnnotatedWith(DBTable.class)) {
if (element instanceof TypeElement) {
// for class
String tempTitle = element.accept(new TableCreationVisitor(), null);
sqlBuilder.append(tempTitle);
// for field
for (Element elementSub : element.getEnclosedElements()) {
if (elementSub instanceof VariableElement &&
elementSub.getKind() == ElementKind.FIELD) {
String tempField = elementSub.accept(new TableCreationVisitor(), null);
sqlBuilder.append(tempField);
}
}
// remove the ","
if (sqlBuilder.length() > 1) {
sqlBuilder.deleteCharAt(sqlBuilder.length() - 1);
}
sqlBuilder.append(");");
// 这里只是打印到控制台, 不输出到文件了.
System.out.println("");
System.out.println(sqlBuilder.toString());
}
}
return false;
}
// 定义 Visitor
private class TableCreationVisitor extends SimpleElementVisitor6<String, String> {
// 对类类型的访问.
@Override
public String visitType(TypeElement typeElement, String s) {
StringBuilder sql = new StringBuilder();
// only deal the class type.
if (typeElement.getKind() == ElementKind.CLASS) {
DBTable dbTable = typeElement.getAnnotation(DBTable.class);
if (dbTable != null) {
sql.append("CREATE TABLE ");
sql.append((dbTable.name().length() < 1) ? typeElement.getSimpleName() : dbTable.name());
sql.append(" (");
}
}
// no action in the default_action
return sql.toString();
}
// 对域的访问
@Override
public String visitVariable(VariableElement e, String s) {
String columnName = "";
StringBuilder sql = new StringBuilder();
// only deal the method type
if (e.getKind() == ElementKind.FIELD) {
SQLInteger sInt = e.getAnnotation(SQLInteger.class);
if (sInt != null) {
// use filed name if annotation-name not specified
if (sInt.name().length() < 1) {
columnName = e.getSimpleName().toString().toUpperCase();
} else {
columnName = sInt.name();
}
sql.append("\n ")
.append(columnName)
.append(" INT")
.append(getConstraints(sInt.constraints()))
.append(",");
}
SQLString sString = e.getAnnotation(SQLString.class);
if (sString != null) {
// use field name if name not specified
if (sString.name().length() < 1) {
columnName = e.getSimpleName().toString().toUpperCase();
} else {
columnName = sString.name();
}
sql.append("\n ")
.append(columnName)
.append(" VARCHAR(").append(sString.value()).append(")")
.append(getConstraints(sString.constraints()))
.append(",");
}
}
return sql.toString();
}
private String getConstraints(Constraints con) {
StringBuilder constrains = new StringBuilder();
if (!con.allowNull()) {
constrains.append(" NOT NULL");
}
if (con.primaryKey()) {
constrains.append(" PRIMARY KEY");
}
if (con.unique()) {
constrains.append(" UNIQUE");
}
return constrains.toString();
}
}
}

     然后我们给一个类添加注解, NOTE: Member 需要在 app Model 下, 不然自己定义的 Processor 没有效果.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// omit package and import
@DBTable(name = "MEMBER")
public class Member {
@SQLString(30)
String firstName;
@SQLString(50)
String lastName;
@SQLInteger()
Integer age;
@SQLString(value = 30, constraints = @Constraints(primaryKey = true))
String handle;
static int memberCount;
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public Integer getAge() {
return age;
}
public String getHandle() {
return handle;
}
@Override
public String toString() {
return handle;
}
}

     编译一下, 然后看一下输出结果.



     可以看到, 数据库访问的 sql 语句.

~感谢捧场,您的支持将鼓励我继续创作~