[原创]牛刀小试——用Groovy实现最简单的ORM(2006.9.25更新)
潜水那么久,也该写点东西跟各位朋友分享一下了:-)
小弟关注Groovy已有数月(您可以到Groovy官方网站 http://groovy.codehaus.org 下载),发现其极具魅力,故在我参加的学校'创新试验项目'中,就用它来实现最简易的ORM,做的非常简单,主要原因是没有时间,因为小弟学业繁重,所以抽出一个下午的时间来实现一个简易版的ORM,数据库用的是MySQL。现在简单说明一下所示代码,将User类的一个实例通过save方法保存到数据库中,然后再根据给定条件通过findBy方法从数据库中取出实例,最后删除一个特定实例。由于深知通过XML文件进行配置的痛苦,所以在设计时没有用到任何XML文件。此程序让程序员只需关注自己要处理的对象,而不用关心数据库方面的东西,简化开发过程。最后我想说明的是,由于时间问题,所以编码方面只注重算法的体现,没有考虑其他方面。下面给出的代码仅供演示及参考(源码已经上传,点击下载):
1
package
edu.ecust.orm
2
3 import groovy.sql.Sql
4 import groovy.text.Template
5 import groovy.text.SimpleTemplateEngine
6
7 public class User {
8 private int id
9 private String name
10 private int age
11
12 public User() {}
13
14 public User( int id) {
15 this .id = id
16 }
17
18 public User( int id, String name, int age) {
19 this.id = id
20 this.name = name
21 this.age = age
22 }
23
24 public int getId() {
25 return id
26 }
27
28 public void setId( int id) {
29 this.id = id
30 }
31
32 public String getName() {
33 return name
34 }
35
36 public void setName(String name) {
37 this.name = name
38 }
39
40 public int getAge() {
41 return age
42 }
43
44 public void setAge( int age) {
45 this.age = age
46 }
47
48
49
50 public String toString() {
51 return name + "(#" + id + ", " + age + ")"
52 }
53 }
54
55 typemap = [ " int " : " INTEGER " , " java.lang.Integer " : " INTEGER " , " long " : " BIGINT " , " java.lang.Long " : " BIGINT " , " short " : " SMALLINT " , " java.lang.Short " : " SMALLINT " , " byte " : " TINYINT " , " java.lang.Byte " : " TINYINT " , " float " : " FLOAT " , " java.lang.Float " : " FLOAT " , " double " : " DOUBLE " , " java.lang.Double " : " DOUBLE " , " java.math.BigDecimal " : " NUMERIC " , " char " : " CHAR(1) " , " java.lang.Character " : " CHAR(1) " , " java.lang.String " : " VARCHAR(50) " , " boolean " : " BIT " , " java.lang.Boolean " : " BIT " ]
56
57 def capitalize(str) {
58 def c = str.charAt( 0 )
59 int asci = ( int )c
60 if (asci > 96 && asci < 123 ) {
61 return (( char )(asci - 32 )).toString() + str.substring( 1 )
62 }
63 }
64
65 def user = new User( 1 , " Daniel " , 21 )
66
67 def connect() {
68 return Sql.newInstance(
69 "jdbc:mysql://localhost:3306/orm" , "root" ,
70 "1106" , "com.mysql.jdbc.Driver" )
71 }
72
73 def pointToLine(str) {
74 return str.replaceAll( "\\p{Punct}" , "_" )
75 }
76
77 def getFields(u) {
78 def clazz = u.getClass()
79 def fields = clazz.getDeclaredFields()
80 def fieldunit = [:]
81 for (f in fields) {
82 def fstr = f.toString()
83 if (fstr.startsWith( "private" )) {
84 def fieldname = fstr.substring(fstr.lastIndexOf( "." ) + 1 )
85 def methodname = "get" + capitalize(fieldname)
86 fieldunit.put(fieldname, clazz.getMethod(methodname).invoke(u))
87 }
88 }
89
90 return fieldunit
91 }
92
93 tablename = "" ;
94 def save(u) {
95 def clazz = u.getClass()
96 def classname = clazz.getName()
97 def classunit = [:]
98 fieldunit = getFields(u)
99 classunit.put(classname, fieldunit)
100 def keySet = classunit.keySet()
101 def it = keySet.iterator()
102 def fields
103 while (it.hasNext()) {
104 tablename = it.next()
105 fields = classunit.get(tablename)
106 }
107 tablename = pointToLine(tablename)
108 def fkeySet = fields.keySet()
109 def fit = fkeySet.iterator()
110 def creationstmt = ""
111
112 def order = [:]
113 def num = 0
114 while (fit.hasNext()) {
115 def fieldname = fit.next()
116 def fieldvalue = fields.get(fieldname)
117 if ( "id" == fieldname) {
118 creationstmt += "id INTEGER PRIMARY KEY not null,"
119 } else {
120 creationstmt += fieldname + " " + typemap.get(fieldvalue.getClass().getName()) + ","
121 }
122 order.put(num++ , fieldname)
123 }
124 creationstmt = creationstmt.substring( 0 , creationstmt.length() - 1 )
125 def valuestr = "("
126 def vkeySet = order.keySet()
127 def vit = vkeySet.iterator()
128 params = "("
129 while (vit.hasNext()) {
130 def elem = vit.next()
131 def v = order.get(elem)
132 valuestr += "'\${" + v + "}',"
133 params += "`" + v + "`,"
134 }
135 valuestr = valuestr.substring( 0 , valuestr.length() - 1 )
136 params = params.substring( 0 , params.length() - 1 )
137 valuestr += ")"
138 params += ")"
139 def insertstmt = "INSERT INTO `" + tablename + "`" + params + " VALUES"
140 insertstmt += valuestr
141 def engine = new SimpleTemplateEngine()
142 def template = engine.createTemplate(insertstmt).make(fields)
143 insertstmt = template.toString()
144 def createstmt = """
145 CREATE TABLE IF NOT EXISTS `""" + tablename + """`
146 (
147 """ + creationstmt + """
148 );
149 """
150 try {
151 def sql = connect()
152 sql.execute(createstmt)
153 sql.execute(insertstmt)
154
155 } catch (Exception e) {
156 int id = u.getId()
157 id++
158 u.setId(id)
159 save(u)
160 //println e.getMessage()
161 }
162
163 return u.getId()
164 }
165
166 def delete(u) {
167 try {
168 def id = u.getId()
169 def sql = connect()
170 def result = sql.execute("DELETE FROM `" + tablename + "` WHERE id=" + id);
171 println "delete operation completed!"
172 } catch (Exception e) {
173 println e.getMessage()
174 }
175 }
176
177 def findBy(classname, condition) {
178 def sql = connect()
179 objs = []
180 sql.eachRow( "select * from " + pointToLine(classname) + " where " + condition) {
181 obj = Class.forName(classname).newInstance()
182
183 def clazz = obj.getClass()
184 def fields = clazz.getDeclaredFields()
185 def methods = clazz.getDeclaredMethods()
186 for (f in fields) {
187 def fstr = f.toString()
188 if (fstr.startsWith( "private" )) {
189 def fieldname = fstr.substring(fstr.lastIndexOf( "." ) + 1 )
190 def setmethodname = "set" + capitalize(fieldname)
191 def setmethod
192 for (m in methods) {
193 def methodname = m.toString()
194 if (methodname.contains(setmethodname)) {
195 setmethod = m
196 }
197 }
198 def fieldvalue = it.getProperty(fieldname)
199 setmethod.invoke(obj, fieldvalue)
200 }
201 }
202
203 objs.add(obj)
204 }
205
206 return objs
207 }
208
209 println " Id of the saved object is " + save(user)
210 println " Found " + findBy( " edu.ecust.orm.User " , " id = '11' " )
211 userToDelete = new User( 12 , " Daniel " , 21 )
212 delete(userToDelete)
213
2
3 import groovy.sql.Sql
4 import groovy.text.Template
5 import groovy.text.SimpleTemplateEngine
6
7 public class User {
8 private int id
9 private String name
10 private int age
11
12 public User() {}
13
14 public User( int id) {
15 this .id = id
16 }
17
18 public User( int id, String name, int age) {
19 this.id = id
20 this.name = name
21 this.age = age
22 }
23
24 public int getId() {
25 return id
26 }
27
28 public void setId( int id) {
29 this.id = id
30 }
31
32 public String getName() {
33 return name
34 }
35
36 public void setName(String name) {
37 this.name = name
38 }
39
40 public int getAge() {
41 return age
42 }
43
44 public void setAge( int age) {
45 this.age = age
46 }
47
48
49
50 public String toString() {
51 return name + "(#" + id + ", " + age + ")"
52 }
53 }
54
55 typemap = [ " int " : " INTEGER " , " java.lang.Integer " : " INTEGER " , " long " : " BIGINT " , " java.lang.Long " : " BIGINT " , " short " : " SMALLINT " , " java.lang.Short " : " SMALLINT " , " byte " : " TINYINT " , " java.lang.Byte " : " TINYINT " , " float " : " FLOAT " , " java.lang.Float " : " FLOAT " , " double " : " DOUBLE " , " java.lang.Double " : " DOUBLE " , " java.math.BigDecimal " : " NUMERIC " , " char " : " CHAR(1) " , " java.lang.Character " : " CHAR(1) " , " java.lang.String " : " VARCHAR(50) " , " boolean " : " BIT " , " java.lang.Boolean " : " BIT " ]
56
57 def capitalize(str) {
58 def c = str.charAt( 0 )
59 int asci = ( int )c
60 if (asci > 96 && asci < 123 ) {
61 return (( char )(asci - 32 )).toString() + str.substring( 1 )
62 }
63 }
64
65 def user = new User( 1 , " Daniel " , 21 )
66
67 def connect() {
68 return Sql.newInstance(
69 "jdbc:mysql://localhost:3306/orm" , "root" ,
70 "1106" , "com.mysql.jdbc.Driver" )
71 }
72
73 def pointToLine(str) {
74 return str.replaceAll( "\\p{Punct}" , "_" )
75 }
76
77 def getFields(u) {
78 def clazz = u.getClass()
79 def fields = clazz.getDeclaredFields()
80 def fieldunit = [:]
81 for (f in fields) {
82 def fstr = f.toString()
83 if (fstr.startsWith( "private" )) {
84 def fieldname = fstr.substring(fstr.lastIndexOf( "." ) + 1 )
85 def methodname = "get" + capitalize(fieldname)
86 fieldunit.put(fieldname, clazz.getMethod(methodname).invoke(u))
87 }
88 }
89
90 return fieldunit
91 }
92
93 tablename = "" ;
94 def save(u) {
95 def clazz = u.getClass()
96 def classname = clazz.getName()
97 def classunit = [:]
98 fieldunit = getFields(u)
99 classunit.put(classname, fieldunit)
100 def keySet = classunit.keySet()
101 def it = keySet.iterator()
102 def fields
103 while (it.hasNext()) {
104 tablename = it.next()
105 fields = classunit.get(tablename)
106 }
107 tablename = pointToLine(tablename)
108 def fkeySet = fields.keySet()
109 def fit = fkeySet.iterator()
110 def creationstmt = ""
111
112 def order = [:]
113 def num = 0
114 while (fit.hasNext()) {
115 def fieldname = fit.next()
116 def fieldvalue = fields.get(fieldname)
117 if ( "id" == fieldname) {
118 creationstmt += "id INTEGER PRIMARY KEY not null,"
119 } else {
120 creationstmt += fieldname + " " + typemap.get(fieldvalue.getClass().getName()) + ","
121 }
122 order.put(num++ , fieldname)
123 }
124 creationstmt = creationstmt.substring( 0 , creationstmt.length() - 1 )
125 def valuestr = "("
126 def vkeySet = order.keySet()
127 def vit = vkeySet.iterator()
128 params = "("
129 while (vit.hasNext()) {
130 def elem = vit.next()
131 def v = order.get(elem)
132 valuestr += "'\${" + v + "}',"
133 params += "`" + v + "`,"
134 }
135 valuestr = valuestr.substring( 0 , valuestr.length() - 1 )
136 params = params.substring( 0 , params.length() - 1 )
137 valuestr += ")"
138 params += ")"
139 def insertstmt = "INSERT INTO `" + tablename + "`" + params + " VALUES"
140 insertstmt += valuestr
141 def engine = new SimpleTemplateEngine()
142 def template = engine.createTemplate(insertstmt).make(fields)
143 insertstmt = template.toString()
144 def createstmt = """
145 CREATE TABLE IF NOT EXISTS `""" + tablename + """`
146 (
147 """ + creationstmt + """
148 );
149 """
150 try {
151 def sql = connect()
152 sql.execute(createstmt)
153 sql.execute(insertstmt)
154
155 } catch (Exception e) {
156 int id = u.getId()
157 id++
158 u.setId(id)
159 save(u)
160 //println e.getMessage()
161 }
162
163 return u.getId()
164 }
165
166 def delete(u) {
167 try {
168 def id = u.getId()
169 def sql = connect()
170 def result = sql.execute("DELETE FROM `" + tablename + "` WHERE id=" + id);
171 println "delete operation completed!"
172 } catch (Exception e) {
173 println e.getMessage()
174 }
175 }
176
177 def findBy(classname, condition) {
178 def sql = connect()
179 objs = []
180 sql.eachRow( "select * from " + pointToLine(classname) + " where " + condition) {
181 obj = Class.forName(classname).newInstance()
182
183 def clazz = obj.getClass()
184 def fields = clazz.getDeclaredFields()
185 def methods = clazz.getDeclaredMethods()
186 for (f in fields) {
187 def fstr = f.toString()
188 if (fstr.startsWith( "private" )) {
189 def fieldname = fstr.substring(fstr.lastIndexOf( "." ) + 1 )
190 def setmethodname = "set" + capitalize(fieldname)
191 def setmethod
192 for (m in methods) {
193 def methodname = m.toString()
194 if (methodname.contains(setmethodname)) {
195 setmethod = m
196 }
197 }
198 def fieldvalue = it.getProperty(fieldname)
199 setmethod.invoke(obj, fieldvalue)
200 }
201 }
202
203 objs.add(obj)
204 }
205
206 return objs
207 }
208
209 println " Id of the saved object is " + save(user)
210 println " Found " + findBy( " edu.ecust.orm.User " , " id = '11' " )
211 userToDelete = new User( 12 , " Daniel " , 21 )
212 delete(userToDelete)
213