Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
0bedabe6
提交
0bedabe6
authored
2月 19, 2011
作者:
james.moger@gmail.com
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
JaQu annotations, model generation, model validation, extended syntax (issue #286)
上级
e0228642
隐藏空白字符变更
内嵌
并排
正在显示
35 个修改的文件
包含
4293 行增加
和
108 行删除
+4293
-108
TestAll.java
h2/src/test/org/h2/test/TestAll.java
+4
-0
AnnotationsTest.java
h2/src/test/org/h2/test/jaqu/AnnotationsTest.java
+157
-0
ModelsTest.java
h2/src/test/org/h2/test/jaqu/ModelsTest.java
+156
-0
ProductAnnotationOnly.java
h2/src/test/org/h2/test/jaqu/ProductAnnotationOnly.java
+83
-0
ProductInheritedAnnotation.java
h2/src/test/org/h2/test/jaqu/ProductInheritedAnnotation.java
+46
-0
ProductMixedAnnotation.java
h2/src/test/org/h2/test/jaqu/ProductMixedAnnotation.java
+83
-0
ProductNoCreateTable.java
h2/src/test/org/h2/test/jaqu/ProductNoCreateTable.java
+48
-0
SamplesTest.java
h2/src/test/org/h2/test/jaqu/SamplesTest.java
+30
-1
SupportedTypes.java
h2/src/test/org/h2/test/jaqu/SupportedTypes.java
+112
-0
UpdateTest.java
h2/src/test/org/h2/test/jaqu/UpdateTest.java
+39
-2
Db.java
h2/src/tools/org/h2/jaqu/Db.java
+148
-12
DbInspector.java
h2/src/tools/org/h2/jaqu/DbInspector.java
+174
-0
DbUpgrader.java
h2/src/tools/org/h2/jaqu/DbUpgrader.java
+68
-0
DbVersion.java
h2/src/tools/org/h2/jaqu/DbVersion.java
+42
-0
Declaration.java
h2/src/tools/org/h2/jaqu/Declaration.java
+21
-0
Define.java
h2/src/tools/org/h2/jaqu/Define.java
+18
-1
IncrementColumn.java
h2/src/tools/org/h2/jaqu/IncrementColumn.java
+46
-0
ModelUtils.java
h2/src/tools/org/h2/jaqu/ModelUtils.java
+343
-0
Query.java
h2/src/tools/org/h2/jaqu/Query.java
+72
-8
QueryWhere.java
h2/src/tools/org/h2/jaqu/QueryWhere.java
+14
-0
SQLDialect.java
h2/src/tools/org/h2/jaqu/SQLDialect.java
+80
-0
SQLStatement.java
h2/src/tools/org/h2/jaqu/SQLStatement.java
+32
-5
SelectTable.java
h2/src/tools/org/h2/jaqu/SelectTable.java
+2
-2
SetColumn.java
h2/src/tools/org/h2/jaqu/SetColumn.java
+39
-0
Table.java
h2/src/tools/org/h2/jaqu/Table.java
+435
-5
TableDefinition.java
h2/src/tools/org/h2/jaqu/TableDefinition.java
+298
-51
TableInspector.java
h2/src/tools/org/h2/jaqu/TableInspector.java
+695
-0
Validation.java
h2/src/tools/org/h2/jaqu/Validation.java
+113
-0
ConstantString.java
h2/src/tools/org/h2/jaqu/bytecode/ConstantString.java
+1
-1
GenerateModels.java
h2/src/tools/org/h2/jaqu/util/GenerateModels.java
+167
-0
JdbcUtils.java
h2/src/tools/org/h2/jaqu/util/JdbcUtils.java
+241
-0
StatementBuilder.java
h2/src/tools/org/h2/jaqu/util/StatementBuilder.java
+137
-0
StatementLogger.java
h2/src/tools/org/h2/jaqu/util/StatementLogger.java
+91
-0
StringUtils.java
h2/src/tools/org/h2/jaqu/util/StringUtils.java
+180
-0
Utils.java
h2/src/tools/org/h2/jaqu/util/Utils.java
+78
-20
没有找到文件。
h2/src/test/org/h2/test/TestAll.java
浏览文件 @
0bedabe6
...
...
@@ -65,7 +65,9 @@ import org.h2.test.db.TestView;
import
org.h2.test.db.TestViewAlterTable
;
import
org.h2.test.db.TestViewDropView
;
import
org.h2.test.jaqu.AliasMapTest
;
import
org.h2.test.jaqu.AnnotationsTest
;
import
org.h2.test.jaqu.ClobTest
;
import
org.h2.test.jaqu.ModelsTest
;
import
org.h2.test.jaqu.SamplesTest
;
import
org.h2.test.jaqu.UpdateTest
;
import
org.h2.test.jdbc.TestBatchUpdates
;
...
...
@@ -588,6 +590,8 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new
ClobTest
().
runTest
(
this
);
new
SamplesTest
().
runTest
(
this
);
new
UpdateTest
().
runTest
(
this
);
new
AnnotationsTest
().
runTest
(
this
);
new
ModelsTest
().
runTest
(
this
);
// jdbc
new
TestBatchUpdates
().
runTest
(
this
);
...
...
h2/src/test/org/h2/test/jaqu/AnnotationsTest.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: James Moger
*/
package
org
.
h2
.
test
.
jaqu
;
import
java.util.List
;
import
org.h2.constant.ErrorCode
;
import
org.h2.jaqu.Db
;
import
org.h2.jdbc.JdbcSQLException
;
import
org.h2.test.TestBase
;
public
class
AnnotationsTest
extends
TestBase
{
/**
* This object represents a database (actually a connection to the database).
*/
//## Java 1.5 begin ##
Db
db
;
//## Java 1.5 end ##
/**
* This method is called when executing this application from the command
* line.
*
* @param args the command line parameters
*/
public
static
void
main
(
String
...
args
)
{
new
AnnotationsTest
().
test
();
}
public
void
test
()
{
//## Java 1.5 begin ##
// EventLogger.activateConsoleLogger();
db
=
Db
.
open
(
"jdbc:h2:mem:"
,
"sa"
,
"sa"
);
db
.
insertAll
(
Product
.
getList
());
db
.
insertAll
(
ProductAnnotationOnly
.
getList
());
db
.
insertAll
(
ProductMixedAnnotation
.
getList
());
testProductAnnotationOnly
();
testProductMixedAnnotation
();
testTrimStringAnnotation
();
testCreateTableIfRequiredAnnotation
();
testColumnInheritanceAnnotation
();
db
.
close
();
// EventLogger.deactivateConsoleLogger();
//## Java 1.5 end ##
}
private
void
testProductAnnotationOnly
()
{
ProductAnnotationOnly
p
=
new
ProductAnnotationOnly
();
assertEquals
(
10
,
db
.
from
(
p
).
selectCount
());
// Test JQColumn.name="cat"
assertEquals
(
2
,
db
.
from
(
p
).
where
(
p
.
category
).
is
(
"Beverages"
).
selectCount
());
// Test JQTable.annotationsOnly=true
// public String unmappedField is ignored by JaQu
assertEquals
(
0
,
db
.
from
(
p
).
where
(
p
.
unmappedField
).
is
(
"unmapped"
).
selectCount
());
// Test JQColumn.autoIncrement=true
// 10 objects, 10 autoIncremented unique values
assertEquals
(
10
,
db
.
from
(
p
).
selectDistinct
(
p
.
autoIncrement
).
size
());
// Test JQTable.primaryKey=id
int
errorCode
=
0
;
try
{
db
.
insertAll
(
ProductAnnotationOnly
.
getList
());
}
catch
(
Throwable
t
)
{
if
(
t
.
getCause
()
instanceof
JdbcSQLException
)
{
JdbcSQLException
s
=
(
JdbcSQLException
)
t
.
getCause
();
errorCode
=
s
.
getErrorCode
();
}
}
assertEquals
(
errorCode
,
ErrorCode
.
DUPLICATE_KEY_1
);
}
private
void
testProductMixedAnnotation
()
{
ProductMixedAnnotation
p
=
new
ProductMixedAnnotation
();
// Test JQColumn.name="cat"
assertEquals
(
2
,
db
.
from
(
p
).
where
(
p
.
category
).
is
(
"Beverages"
).
selectCount
());
// Test JQTable.annotationsOnly=false
// public String mappedField is reflectively mapped by JaQu
assertEquals
(
10
,
db
.
from
(
p
).
where
(
p
.
mappedField
).
is
(
"mapped"
).
selectCount
());
// Test JQColumn.primaryKey=true
int
errorCode
=
0
;
try
{
db
.
insertAll
(
ProductMixedAnnotation
.
getList
());
}
catch
(
Throwable
t
)
{
if
(
t
.
getCause
()
instanceof
JdbcSQLException
)
{
JdbcSQLException
s
=
(
JdbcSQLException
)
t
.
getCause
();
errorCode
=
s
.
getErrorCode
();
}
}
assertEquals
(
errorCode
,
ErrorCode
.
DUPLICATE_KEY_1
);
}
private
void
testTrimStringAnnotation
()
{
ProductAnnotationOnly
p
=
new
ProductAnnotationOnly
();
ProductAnnotationOnly
prod
=
db
.
from
(
p
).
selectFirst
();
String
oldValue
=
prod
.
category
;
String
newValue
=
"01234567890123456789"
;
prod
.
category
=
newValue
;
// 2 chars exceeds field max
db
.
update
(
prod
);
ProductAnnotationOnly
newProd
=
db
.
from
(
p
)
.
where
(
p
.
productId
)
.
is
(
prod
.
productId
)
.
selectFirst
();
assertEquals
(
newValue
.
substring
(
0
,
15
),
newProd
.
category
);
newProd
.
category
=
oldValue
;
db
.
update
(
newProd
);
}
private
void
testColumnInheritanceAnnotation
()
{
ProductInheritedAnnotation
table
=
new
ProductInheritedAnnotation
();
Db
db
=
Db
.
open
(
"jdbc:h2:mem:"
,
"sa"
,
"sa"
);
List
<
ProductInheritedAnnotation
>
inserted
=
ProductInheritedAnnotation
.
getData
();
db
.
insertAll
(
inserted
);
List
<
ProductInheritedAnnotation
>
retrieved
=
db
.
from
(
table
).
select
();
for
(
int
j
=
0
;
j
<
retrieved
.
size
();
j
++)
{
ProductInheritedAnnotation
i
=
inserted
.
get
(
j
);
ProductInheritedAnnotation
r
=
retrieved
.
get
(
j
);
assertEquals
(
i
.
category
,
r
.
category
);
assertEquals
(
i
.
mappedField
,
r
.
mappedField
);
assertEquals
(
i
.
unitsInStock
,
r
.
unitsInStock
);
assertEquals
(
i
.
unitPrice
,
r
.
unitPrice
);
assertEquals
(
i
.
name
(),
r
.
name
());
assertEquals
(
i
.
id
(),
r
.
id
());
}
db
.
close
();
}
private
void
testCreateTableIfRequiredAnnotation
()
{
// Tests JQTable.createTableIfRequired=false
int
errorCode
=
0
;
try
{
Db
noCreateDb
=
Db
.
open
(
"jdbc:h2:mem:"
,
"sa"
,
"sa"
);
noCreateDb
.
insertAll
(
ProductNoCreateTable
.
getList
());
noCreateDb
.
close
();
}
catch
(
Throwable
e
)
{
if
(
e
.
getCause
()
instanceof
JdbcSQLException
)
{
JdbcSQLException
error
=
(
JdbcSQLException
)
e
.
getCause
();
errorCode
=
error
.
getErrorCode
();
}
}
assertTrue
(
errorCode
==
ErrorCode
.
TABLE_OR_VIEW_NOT_FOUND_1
);
}
}
h2/src/test/org/h2/test/jaqu/ModelsTest.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: James Moger
*/
package
org
.
h2
.
test
.
jaqu
;
import
java.util.List
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
org.h2.jaqu.Db
;
import
org.h2.jaqu.DbInspector
;
import
org.h2.jaqu.DbUpgrader
;
import
org.h2.jaqu.DbVersion
;
import
org.h2.jaqu.Table.JQDatabase
;
import
org.h2.jaqu.Validation
;
import
org.h2.test.TestBase
;
import
org.h2.test.jaqu.SupportedTypes.SupportedTypes2
;
public
class
ModelsTest
extends
TestBase
{
/**
* This object represents a database (actually a connection to the database).
*/
//## Java 1.5 begin ##
Db
db
;
//## Java 1.5 end ##
/**
* This method is called when executing this application from the command
* line.
*
* @param args the command line parameters
*/
public
static
void
main
(
String
...
args
)
{
new
ModelsTest
().
test
();
}
public
void
test
()
{
//## Java 1.5 begin ##
db
=
Db
.
open
(
"jdbc:h2:mem:"
,
"sa"
,
"sa"
);
db
.
insertAll
(
Product
.
getList
());
db
.
insertAll
(
ProductAnnotationOnly
.
getList
());
db
.
insertAll
(
ProductMixedAnnotation
.
getList
());
testValidateModels
();
testSupportedTypes
();
testModelGeneration
();
testDatabaseUpgrade
();
testTableUpgrade
();
db
.
close
();
//## Java 1.5 end ##
}
private
void
testValidateModels
()
{
boolean
verbose
=
false
;
DbInspector
inspector
=
new
DbInspector
(
db
);
validateModel
(
inspector
,
new
Product
(),
verbose
);
validateModel
(
inspector
,
new
ProductAnnotationOnly
(),
verbose
);
validateModel
(
inspector
,
new
ProductMixedAnnotation
(),
verbose
);
}
private
void
validateModel
(
DbInspector
inspector
,
Object
o
,
boolean
verbose
)
{
List
<
Validation
>
remarks
=
inspector
.
validateModel
(
o
,
false
);
if
(
verbose
&&
remarks
.
size
()
>
0
)
{
System
.
out
.
println
(
"Validation Remarks for "
+
o
.
getClass
().
getName
());
System
.
out
.
println
(
"============================================="
);
for
(
Validation
remark:
remarks
)
System
.
out
.
println
(
remark
);
System
.
out
.
println
();
}
for
(
Validation
remark:
remarks
)
assertFalse
(
remark
.
toString
(),
remark
.
isError
());
}
private
void
testSupportedTypes
()
{
List
<
SupportedTypes
>
original
=
SupportedTypes
.
createList
();
db
.
insertAll
(
original
);
List
<
SupportedTypes
>
retrieved
=
db
.
from
(
SupportedTypes
.
SAMPLE
).
select
();
assertEquals
(
original
.
size
(),
retrieved
.
size
());
for
(
int
i
=
0
;
i
<
original
.
size
();
i
++)
{
SupportedTypes
o
=
original
.
get
(
i
);
SupportedTypes
r
=
retrieved
.
get
(
i
);
assertTrue
(
o
.
equivalentTo
(
r
));
}
}
private
void
testModelGeneration
()
{
DbInspector
inspector
=
new
DbInspector
(
db
);
List
<
String
>
models
=
inspector
.
generateModel
(
null
,
"SupportedTypes"
,
"org.h2.test.jaqu"
,
true
,
true
);
assertEquals
(
1
,
models
.
size
());
// a poor test, but a start
assertEquals
(
1364
,
models
.
get
(
0
).
length
());
}
private
void
testDatabaseUpgrade
()
{
Db
db
=
Db
.
open
(
"jdbc:h2:mem:"
,
"sa"
,
"sa"
);
// Insert a Database version record
db
.
insert
(
new
DbVersion
(
1
));
TestDbUpgrader
dbUpgrader
=
new
TestDbUpgrader
();
db
.
setDbUpgrader
(
dbUpgrader
);
List
<
SupportedTypes
>
original
=
SupportedTypes
.
createList
();
db
.
insertAll
(
original
);
assertEquals
(
1
,
dbUpgrader
.
oldVersion
.
get
());
assertEquals
(
2
,
dbUpgrader
.
newVersion
.
get
());
db
.
close
();
}
private
void
testTableUpgrade
()
{
Db
db
=
Db
.
open
(
"jdbc:h2:mem:"
,
"sa"
,
"sa"
);
// Insert first, this will create version record automatically
List
<
SupportedTypes
>
original
=
SupportedTypes
.
createList
();
db
.
insertAll
(
original
);
// Reset the dbUpgrader (clears updatecheck cache)
TestDbUpgrader
dbUpgrader
=
new
TestDbUpgrader
();
db
.
setDbUpgrader
(
dbUpgrader
);
SupportedTypes2
s2
=
new
SupportedTypes2
();
List
<
SupportedTypes2
>
types
=
db
.
from
(
s2
).
select
();
assertEquals
(
10
,
types
.
size
());
assertEquals
(
1
,
dbUpgrader
.
oldVersion
.
get
());
assertEquals
(
2
,
dbUpgrader
.
newVersion
.
get
());
db
.
close
();
}
@JQDatabase
(
version
=
2
)
private
class
TestDbUpgrader
implements
DbUpgrader
{
final
AtomicInteger
oldVersion
=
new
AtomicInteger
(
0
);
final
AtomicInteger
newVersion
=
new
AtomicInteger
(
0
);
@Override
public
boolean
upgradeTable
(
Db
db
,
String
schema
,
String
table
,
int
fromVersion
,
int
toVersion
)
{
// Sample DbUpgrader just claims success on upgrade request
oldVersion
.
set
(
fromVersion
);
newVersion
.
set
(
toVersion
);
return
true
;
}
@Override
public
boolean
upgradeDatabase
(
Db
db
,
int
fromVersion
,
int
toVersion
)
{
// Sample DbUpgrader just claims success on upgrade request
oldVersion
.
set
(
fromVersion
);
newVersion
.
set
(
toVersion
);
return
true
;
}
};
}
h2/src/test/org/h2/test/jaqu/ProductAnnotationOnly.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, Version
* 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). Initial Developer: H2 Group
*/
package
org
.
h2
.
test
.
jaqu
;
// ## Java 1.5 begin ##
import
java.util.Arrays
;
import
java.util.List
;
import
org.h2.jaqu.Table.JQColumn
;
import
org.h2.jaqu.Table.JQIndex
;
import
org.h2.jaqu.Table.JQTable
;
/**
* A table containing product data.
*/
// ## Java 1.5 begin ##
@JQTable
(
name
=
"AnnotatedProduct"
,
primaryKey
=
"id"
)
@JQIndex
(
standard
=
"name, cat"
)
public
class
ProductAnnotationOnly
{
@JQColumn
(
name
=
"id"
)
Integer
productId
;
@JQColumn
(
name
=
"name"
)
private
String
productName
;
@JQColumn
(
name
=
"cat"
,
maxLength
=
15
,
trimString
=
true
)
String
category
;
@SuppressWarnings
(
"unused"
)
@JQColumn
private
Double
unitPrice
;
@JQColumn
private
Integer
unitsInStock
;
@JQColumn
(
autoIncrement
=
true
)
public
Integer
autoIncrement
;
public
String
unmappedField
;
public
ProductAnnotationOnly
()
{
// public constructor
}
private
ProductAnnotationOnly
(
int
productId
,
String
productName
,
String
category
,
double
unitPrice
,
int
unitsInStock
,
String
unmappedField
)
{
this
.
productId
=
productId
;
this
.
productName
=
productName
;
this
.
category
=
category
;
this
.
unitPrice
=
unitPrice
;
this
.
unitsInStock
=
unitsInStock
;
this
.
unmappedField
=
unmappedField
;
}
private
static
ProductAnnotationOnly
create
(
int
productId
,
String
productName
,
String
category
,
double
unitPrice
,
int
unitsInStock
,
String
unmappedField
)
{
return
new
ProductAnnotationOnly
(
productId
,
productName
,
category
,
unitPrice
,
unitsInStock
,
unmappedField
);
}
public
static
List
<
ProductAnnotationOnly
>
getList
()
{
String
unmappedField
=
"unmapped"
;
ProductAnnotationOnly
[]
list
=
{
create
(
1
,
"Chai"
,
"Beverages"
,
18
,
39
,
unmappedField
),
create
(
2
,
"Chang"
,
"Beverages"
,
19.0
,
17
,
unmappedField
),
create
(
3
,
"Aniseed Syrup"
,
"Condiments"
,
10.0
,
13
,
unmappedField
),
create
(
4
,
"Chef Anton's Cajun Seasoning"
,
"Condiments"
,
22.0
,
53
,
unmappedField
),
create
(
5
,
"Chef Anton's Gumbo Mix"
,
"Condiments"
,
21.3500
,
0
,
unmappedField
),
create
(
6
,
"Grandma's Boysenberry Spread"
,
"Condiments"
,
25.0
,
120
,
unmappedField
),
create
(
7
,
"Uncle Bob's Organic Dried Pears"
,
"Produce"
,
30.0
,
15
,
unmappedField
),
create
(
8
,
"Northwoods Cranberry Sauce"
,
"Condiments"
,
40.0
,
6
,
unmappedField
),
create
(
9
,
"Mishi Kobe Niku"
,
"Meat/Poultry"
,
97.0
,
29
,
unmappedField
),
create
(
10
,
"Ikura"
,
"Seafood"
,
31.0
,
31
,
unmappedField
),
};
return
Arrays
.
asList
(
list
);
}
public
String
toString
()
{
return
productName
+
": "
+
unitsInStock
;
}
}
// ## Java 1.5 end ##
h2/src/test/org/h2/test/jaqu/ProductInheritedAnnotation.java
0 → 100644
浏览文件 @
0bedabe6
package
org
.
h2
.
test
.
jaqu
;
import
java.util.Arrays
;
import
java.util.List
;
import
org.h2.jaqu.Table.JQTable
;
/**
* This class inherits all its fields from a parent class which has annotated
* columns. The JQTable annotation of the parent class is ignored and only
* the JQTable annotation of this class matters.
* <p>
* However, this table inherits JQColumns from its super class.
*/
@JQTable
(
inheritColumns
=
true
,
annotationsOnly
=
false
)
public
class
ProductInheritedAnnotation
extends
ProductMixedAnnotation
{
public
ProductInheritedAnnotation
()
{
// public constructor
}
private
ProductInheritedAnnotation
(
int
productId
,
String
productName
,
String
category
,
double
unitPrice
,
int
unitsInStock
,
String
mappedField
)
{
super
(
productId
,
productName
,
category
,
unitPrice
,
unitsInStock
,
mappedField
);
}
private
static
ProductInheritedAnnotation
create
(
int
productId
,
String
productName
,
String
category
,
double
unitPrice
,
int
unitsInStock
,
String
mappedField
)
{
return
new
ProductInheritedAnnotation
(
productId
,
productName
,
category
,
unitPrice
,
unitsInStock
,
mappedField
);
}
public
static
List
<
ProductInheritedAnnotation
>
getData
()
{
String
mappedField
=
"mapped"
;
ProductInheritedAnnotation
[]
list
=
{
create
(
1
,
"Chai"
,
"Beverages"
,
18
,
39
,
mappedField
),
create
(
2
,
"Chang"
,
"Beverages"
,
19.0
,
17
,
mappedField
),
create
(
3
,
"Aniseed Syrup"
,
"Condiments"
,
10.0
,
13
,
mappedField
),
create
(
4
,
"Chef Anton's Cajun Seasoning"
,
"Condiments"
,
22.0
,
53
,
mappedField
),
create
(
5
,
"Chef Anton's Gumbo Mix"
,
"Condiments"
,
21.3500
,
0
,
mappedField
),
create
(
6
,
"Grandma's Boysenberry Spread"
,
"Condiments"
,
25.0
,
120
,
mappedField
),
create
(
7
,
"Uncle Bob's Organic Dried Pears"
,
"Produce"
,
30.0
,
15
,
mappedField
),
create
(
8
,
"Northwoods Cranberry Sauce"
,
"Condiments"
,
40.0
,
6
,
mappedField
),
create
(
9
,
"Mishi Kobe Niku"
,
"Meat/Poultry"
,
97.0
,
29
,
mappedField
),
create
(
10
,
"Ikura"
,
"Seafood"
,
31.0
,
31
,
mappedField
),
};
return
Arrays
.
asList
(
list
);
}
}
h2/src/test/org/h2/test/jaqu/ProductMixedAnnotation.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, Version
* 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). Initial Developer: H2 Group
*/
package
org
.
h2
.
test
.
jaqu
;
// ## Java 1.5 begin ##
import
java.util.Arrays
;
import
java.util.List
;
import
org.h2.jaqu.Table.JQColumn
;
import
org.h2.jaqu.Table.JQIndex
;
import
org.h2.jaqu.Table.JQTable
;
/**
* A table containing product data.
*/
// ## Java 1.5 begin ##
@JQTable
(
annotationsOnly
=
false
)
@JQIndex
(
standard
=
"name,cat"
)
public
class
ProductMixedAnnotation
{
@JQColumn
(
name
=
"id"
,
primaryKey
=
true
)
private
Integer
productId
;
@JQColumn
(
name
=
"name"
)
private
String
productName
;
@JQColumn
(
name
=
"cat"
,
maxLength
=
255
)
String
category
;
public
Double
unitPrice
;
public
Integer
unitsInStock
;
public
String
mappedField
;
public
ProductMixedAnnotation
()
{
// public constructor
}
protected
ProductMixedAnnotation
(
int
productId
,
String
productName
,
String
category
,
double
unitPrice
,
int
unitsInStock
,
String
mappedField
)
{
this
.
productId
=
productId
;
this
.
productName
=
productName
;
this
.
category
=
category
;
this
.
unitPrice
=
unitPrice
;
this
.
unitsInStock
=
unitsInStock
;
this
.
mappedField
=
mappedField
;
}
private
static
ProductMixedAnnotation
create
(
int
productId
,
String
productName
,
String
category
,
double
unitPrice
,
int
unitsInStock
,
String
mappedField
)
{
return
new
ProductMixedAnnotation
(
productId
,
productName
,
category
,
unitPrice
,
unitsInStock
,
mappedField
);
}
public
static
List
<
ProductMixedAnnotation
>
getList
()
{
String
mappedField
=
"mapped"
;
ProductMixedAnnotation
[]
list
=
{
create
(
1
,
"Chai"
,
"Beverages"
,
18
,
39
,
mappedField
),
create
(
2
,
"Chang"
,
"Beverages"
,
19.0
,
17
,
mappedField
),
create
(
3
,
"Aniseed Syrup"
,
"Condiments"
,
10.0
,
13
,
mappedField
),
create
(
4
,
"Chef Anton's Cajun Seasoning"
,
"Condiments"
,
22.0
,
53
,
mappedField
),
create
(
5
,
"Chef Anton's Gumbo Mix"
,
"Condiments"
,
21.3500
,
0
,
mappedField
),
create
(
6
,
"Grandma's Boysenberry Spread"
,
"Condiments"
,
25.0
,
120
,
mappedField
),
create
(
7
,
"Uncle Bob's Organic Dried Pears"
,
"Produce"
,
30.0
,
15
,
mappedField
),
create
(
8
,
"Northwoods Cranberry Sauce"
,
"Condiments"
,
40.0
,
6
,
mappedField
),
create
(
9
,
"Mishi Kobe Niku"
,
"Meat/Poultry"
,
97.0
,
29
,
mappedField
),
create
(
10
,
"Ikura"
,
"Seafood"
,
31.0
,
31
,
mappedField
),
};
return
Arrays
.
asList
(
list
);
}
public
String
toString
()
{
return
productName
+
": "
+
unitsInStock
;
}
public
int
id
()
{
return
productId
;
}
public
String
name
()
{
return
productName
;
}
}
// ## Java 1.5 end ##
h2/src/test/org/h2/test/jaqu/ProductNoCreateTable.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, Version
* 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). Initial Developer: H2 Group
*/
package
org
.
h2
.
test
.
jaqu
;
// ## Java 1.5 begin ##
import
java.util.Arrays
;
import
java.util.List
;
import
org.h2.jaqu.Table.JQColumn
;
import
org.h2.jaqu.Table.JQTable
;
/**
* A table containing product data.
*/
// ## Java 1.5 begin ##
@JQTable
(
createIfRequired
=
false
)
public
class
ProductNoCreateTable
{
@SuppressWarnings
(
"unused"
)
@JQColumn
(
name
=
"id"
)
private
Integer
productId
;
@SuppressWarnings
(
"unused"
)
@JQColumn
(
name
=
"name"
)
private
String
productName
;
public
ProductNoCreateTable
()
{
// public constructor
}
private
ProductNoCreateTable
(
int
productId
,
String
productName
)
{
this
.
productId
=
productId
;
this
.
productName
=
productName
;
}
private
static
ProductNoCreateTable
create
(
int
productId
,
String
productName
)
{
return
new
ProductNoCreateTable
(
productId
,
productName
);
}
public
static
List
<
ProductNoCreateTable
>
getList
()
{
ProductNoCreateTable
[]
list
=
{
create
(
1
,
"Chai"
),
create
(
2
,
"Chang"
)
};
return
Arrays
.
asList
(
list
);
}
}
// ## Java 1.5 end ##
h2/src/test/org/h2/test/jaqu/SamplesTest.java
浏览文件 @
0bedabe6
...
...
@@ -9,9 +9,14 @@ package org.h2.test.jaqu;
import
static
org
.
h2
.
jaqu
.
Function
.
count
;
import
static
org
.
h2
.
jaqu
.
Function
.
isNull
;
import
static
org
.
h2
.
jaqu
.
Function
.
length
;
import
static
org
.
h2
.
jaqu
.
Function
.*;
import
static
org
.
h2
.
jaqu
.
Function
.
max
;
import
static
org
.
h2
.
jaqu
.
Function
.
min
;
import
static
org
.
h2
.
jaqu
.
Function
.
not
;
import
static
org
.
h2
.
jaqu
.
Function
.
sum
;
import
java.math.BigDecimal
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Set
;
import
org.h2.jaqu.Db
;
import
org.h2.jaqu.Filter
;
import
org.h2.test.TestBase
;
...
...
@@ -73,6 +78,8 @@ public class SamplesTest extends TestBase {
testWhereSimple2
();
testWhereSimple3
();
testReverseColumns
();
testLimitOffset
();
testKeyRetrieval
();
db
.
close
();
//## Java 1.5 end ##
}
...
...
@@ -265,6 +272,9 @@ public class SamplesTest extends TestBase {
deleted
=
db
.
from
(
p
).
delete
();
assertEquals
(
9
,
deleted
);
db
.
insertAll
(
Product
.
getList
());
db
.
deleteAll
(
Product
.
getList
());
assertEquals
(
0
,
db
.
from
(
p
).
selectCount
());
db
.
insertAll
(
Product
.
getList
());
}
private
void
testOrAndNot
()
{
...
...
@@ -376,6 +386,25 @@ public class SamplesTest extends TestBase {
assertEquals
(
1
,
count
);
}
private
void
testLimitOffset
()
{
Set
<
Integer
>
ids
=
new
HashSet
<
Integer
>();
Product
p
=
new
Product
();
for
(
int
i
=
0
;
i
<
5
;
i
++)
{
List
<
Product
>
products
=
db
.
from
(
p
).
limit
(
2
).
offset
(
2
*
i
).
select
();
assertTrue
(
products
.
size
()
==
2
);
for
(
Product
prod:
products
)
assertTrue
(
"Failed to add product id. Duplicate?"
,
ids
.
add
(
prod
.
productId
));
}
}
private
void
testKeyRetrieval
()
{
List
<
SupportedTypes
>
list
=
SupportedTypes
.
createList
();
List
<
Long
>
keys
=
db
.
insertAllAndGetKeys
(
list
);
Set
<
Long
>
uniqueKeys
=
new
HashSet
<
Long
>();
for
(
Long
l:
keys
)
assertTrue
(
"Failed to add key. Duplicate?"
,
uniqueKeys
.
add
(
l
));
}
//## Java 1.5 end ##
/**
...
...
h2/src/test/org/h2/test/jaqu/SupportedTypes.java
0 → 100644
浏览文件 @
0bedabe6
package
org
.
h2
.
test
.
jaqu
;
import
java.math.BigDecimal
;
import
java.util.List
;
import
java.util.Random
;
import
org.h2.jaqu.Table.JQColumn
;
import
org.h2.jaqu.Table.JQTable
;
import
org.h2.util.New
;
@JQTable
(
strictTypeMapping
=
true
,
version
=
1
)
public
class
SupportedTypes
{
@JQColumn
(
primaryKey
=
true
,
autoIncrement
=
true
)
public
Integer
id
;
@JQColumn
private
Boolean
myBool
=
false
;
@JQColumn
private
Byte
myByte
=
2
;
@JQColumn
private
Short
myShort
;
@JQColumn
private
Integer
myInteger
;
@JQColumn
private
Long
myLong
;
@JQColumn
private
Float
myFloat
=
1.0f
;
@JQColumn
private
Double
myDouble
;
@JQColumn
private
BigDecimal
myBigDecimal
;
@JQColumn
private
String
myString
;
@JQColumn
private
java
.
util
.
Date
myUtilDate
;
@JQColumn
private
java
.
sql
.
Date
mySqlDate
;
@JQColumn
private
java
.
sql
.
Time
mySqlTime
;
@JQColumn
private
java
.
sql
.
Timestamp
mySqlTimestamp
;
static
SupportedTypes
SAMPLE
=
new
SupportedTypes
();
static
List
<
SupportedTypes
>
createList
()
{
List
<
SupportedTypes
>
list
=
New
.
arrayList
();
for
(
int
i
=
0
;
i
<
10
;
i
++)
list
.
add
(
randomValue
());
return
list
;
}
static
SupportedTypes
randomValue
()
{
Random
rand
=
new
Random
();
SupportedTypes
s
=
new
SupportedTypes
();
s
.
myBool
=
new
Boolean
(
rand
.
nextBoolean
());
s
.
myByte
=
new
Byte
((
byte
)
rand
.
nextInt
(
Byte
.
MAX_VALUE
));
s
.
myShort
=
new
Short
((
short
)
rand
.
nextInt
(
Short
.
MAX_VALUE
));
s
.
myInteger
=
new
Integer
(
rand
.
nextInt
());
s
.
myLong
=
new
Long
(
rand
.
nextLong
());
s
.
myFloat
=
new
Float
(
rand
.
nextFloat
());
s
.
myDouble
=
new
Double
(
rand
.
nextDouble
());
s
.
myBigDecimal
=
new
BigDecimal
(
rand
.
nextDouble
());
s
.
myString
=
Long
.
toHexString
(
rand
.
nextLong
());
s
.
myUtilDate
=
new
java
.
util
.
Date
(
rand
.
nextLong
());
s
.
mySqlDate
=
new
java
.
sql
.
Date
(
rand
.
nextLong
());
s
.
mySqlTime
=
new
java
.
sql
.
Time
(
rand
.
nextLong
());
s
.
mySqlTimestamp
=
new
java
.
sql
.
Timestamp
(
rand
.
nextLong
());
return
s
;
}
public
boolean
equivalentTo
(
SupportedTypes
s
)
{
boolean
same
=
true
;
same
&=
myBool
.
equals
(
s
.
myBool
);
same
&=
myByte
.
equals
(
s
.
myByte
);
same
&=
myShort
.
equals
(
s
.
myShort
);
same
&=
myInteger
.
equals
(
s
.
myInteger
);
same
&=
myLong
.
equals
(
s
.
myLong
);
same
&=
myFloat
.
equals
(
s
.
myFloat
);
same
&=
myDouble
.
equals
(
s
.
myDouble
);
same
&=
myBigDecimal
.
equals
(
s
.
myBigDecimal
);
same
&=
myUtilDate
.
getTime
()
==
s
.
myUtilDate
.
getTime
();
same
&=
mySqlTimestamp
.
getTime
()
==
s
.
mySqlTimestamp
.
getTime
();
same
&=
mySqlDate
.
toString
().
equals
(
s
.
mySqlDate
.
toString
());
same
&=
mySqlTime
.
toString
().
equals
(
s
.
mySqlTime
.
toString
());
return
same
;
}
/**
* Class to demonstrate TableUpdater
*
*/
@JQTable
(
name
=
"SupportedTypes"
,
version
=
2
,
inheritColumns
=
true
,
strictTypeMapping
=
true
)
public
static
class
SupportedTypes2
extends
SupportedTypes
{
public
SupportedTypes2
()
{
}
}
}
h2/src/test/org/h2/test/jaqu/UpdateTest.java
浏览文件 @
0bedabe6
...
...
@@ -6,11 +6,11 @@
*/
package
org
.
h2
.
test
.
jaqu
;
import
static
java
.
sql
.
Date
.
valueOf
;
import
org.h2.jaqu.Db
;
import
org.h2.jaqu.util.StatementLogger
;
import
org.h2.test.TestBase
;
import
static
java
.
sql
.
Date
.
valueOf
;
/**
* Tests the Db.update() function.
*
...
...
@@ -31,6 +31,8 @@ public class UpdateTest extends TestBase {
}
public
void
test
()
throws
Exception
{
// EventLogger.activateConsoleLogger();
db
=
Db
.
open
(
"jdbc:h2:mem:"
,
"sa"
,
"sa"
);
db
.
insertAll
(
Product
.
getList
());
db
.
insertAll
(
Customer
.
getList
());
...
...
@@ -40,8 +42,10 @@ public class UpdateTest extends TestBase {
testSimpleUpdateWithCombinedPrimaryKey
();
testSimpleMerge
();
testSimpleMergeWithCombinedPrimaryKey
();
testSetColumns
();
db
.
close
();
// EventLogger.deactivateConsoleLogger();
}
private
void
testSimpleUpdate
()
{
...
...
@@ -111,5 +115,38 @@ public class UpdateTest extends TestBase {
ourOrder
.
orderDate
=
valueOf
(
"2007-01-02"
);
db
.
merge
(
ourOrder
);
}
private
void
testSetColumns
()
{
Product
p
=
new
Product
();
Product
original
=
db
.
from
(
p
).
where
(
p
.
productId
).
is
(
1
).
selectFirst
();
// SetColumn on String and Double
db
.
from
(
p
)
.
set
(
p
.
productName
).
to
(
"updated"
)
.
increment
(
p
.
unitPrice
).
by
(
3.14
)
.
increment
(
p
.
unitsInStock
).
by
(
2
)
.
where
(
p
.
productId
)
.
is
(
1
).
update
();
// Confirm fields were properly updated
Product
revised
=
db
.
from
(
p
).
where
(
p
.
productId
).
is
(
1
).
selectFirst
();
assertEquals
(
"updated"
,
revised
.
productName
);
assertEquals
(
original
.
unitPrice
+
3.14
,
revised
.
unitPrice
);
assertEquals
(
original
.
unitsInStock
+
2
,
revised
.
unitsInStock
.
intValue
());
// Restore fields
db
.
from
(
p
)
.
set
(
p
.
productName
).
to
(
original
.
productName
)
.
set
(
p
.
unitPrice
).
to
(
original
.
unitPrice
)
.
increment
(
p
.
unitsInStock
).
by
(-
2
)
.
where
(
p
.
productId
).
is
(
1
).
update
();
// Confirm fields were properly restored
Product
restored
=
db
.
from
(
p
).
where
(
p
.
productId
).
is
(
1
).
selectFirst
();
assertEquals
(
original
.
productName
,
restored
.
productName
);
assertEquals
(
original
.
unitPrice
,
restored
.
unitPrice
);
assertEquals
(
original
.
unitsInStock
,
restored
.
unitsInStock
);
}
}
h2/src/tools/org/h2/jaqu/Db.java
浏览文件 @
0bedabe6
...
...
@@ -12,16 +12,21 @@ import java.sql.PreparedStatement;
import
java.sql.ResultSet
;
import
java.sql.SQLException
;
import
java.sql.Statement
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Properties
;
import
java.util.Set
;
import
javax.sql.DataSource
;
import
org.h2.jaqu.DbUpgrader.DefaultDbUpgrader
;
import
org.h2.jaqu.SQLDialect.DefaultSQLDialect
;
import
org.h2.jaqu.Table.JQDatabase
;
import
org.h2.jaqu.Table.JQTable
;
import
org.h2.jaqu.util.JdbcUtils
;
import
org.h2.jaqu.util.StringUtils
;
import
org.h2.jaqu.util.Utils
;
import
org.h2.jaqu.util.WeakIdentityHashMap
;
import
org.h2.util.JdbcUtils
;
//## Java 1.5 end ##
/**
* This class represents a connection to a database.
...
...
@@ -41,9 +46,18 @@ public class Db {
private
final
Connection
conn
;
private
final
Map
<
Class
<?>,
TableDefinition
<?>>
classMap
=
Utils
.
newHashMap
();
Db
(
Connection
conn
)
{
private
final
SQLDialect
dialect
;
private
DbUpgrader
dbUpgrader
=
new
DefaultDbUpgrader
();
private
final
Set
<
Class
<?>>
upgradeChecked
=
Utils
.
newConcurrentHashSet
();
public
Db
(
Connection
conn
)
{
this
.
conn
=
conn
;
dialect
=
getDialect
(
conn
.
getClass
().
getCanonicalName
());
}
SQLDialect
getDialect
(
String
clazz
)
{
// TODO add special cases here
return
new
DefaultSQLDialect
();
}
static
<
X
>
X
registerToken
(
X
x
,
Token
token
)
{
...
...
@@ -105,27 +119,105 @@ public class Db {
public
<
T
>
void
insert
(
T
t
)
{
Class
<?>
clazz
=
t
.
getClass
();
define
(
clazz
).
createTableIfRequired
(
this
).
insert
(
this
,
t
);
upgradeDb
().
define
(
clazz
).
createTableIfRequired
(
this
).
insert
(
this
,
t
,
false
);
}
public
<
T
>
long
insertAndGetKey
(
T
t
)
{
Class
<?>
clazz
=
t
.
getClass
();
return
upgradeDb
().
define
(
clazz
).
createTableIfRequired
(
this
).
insert
(
this
,
t
,
true
);
}
public
<
T
>
void
merge
(
T
t
)
{
Class
<?>
clazz
=
t
.
getClass
();
define
(
clazz
).
createTableIfRequired
(
this
).
merge
(
this
,
t
);
upgradeDb
().
define
(
clazz
).
createTableIfRequired
(
this
).
merge
(
this
,
t
);
}
public
<
T
>
void
update
(
T
t
)
{
Class
<?>
clazz
=
t
.
getClass
();
define
(
clazz
).
createTableIfRequired
(
this
).
update
(
this
,
t
);
upgradeDb
().
define
(
clazz
).
createTableIfRequired
(
this
).
update
(
this
,
t
);
}
public
<
T
>
void
delete
(
T
t
)
{
Class
<?>
clazz
=
t
.
getClass
();
upgradeDb
().
define
(
clazz
).
createTableIfRequired
(
this
).
delete
(
this
,
t
);
}
public
<
T
extends
Object
>
Query
<
T
>
from
(
T
alias
)
{
Class
<?>
clazz
=
alias
.
getClass
();
define
(
clazz
).
createTableIfRequired
(
this
);
upgradeDb
().
define
(
clazz
).
createTableIfRequired
(
this
);
return
Query
.
from
(
this
,
alias
);
}
Db
upgradeDb
()
{
if
(!
upgradeChecked
.
contains
(
dbUpgrader
.
getClass
()))
{
// Flag as checked immediately because calls are nested.
upgradeChecked
.
add
(
dbUpgrader
.
getClass
());
JQDatabase
model
=
dbUpgrader
.
getClass
().
getAnnotation
(
JQDatabase
.
class
);
if
(
model
.
version
()
>
0
)
{
DbVersion
v
=
new
DbVersion
();
DbVersion
dbVersion
=
// (SCHEMA="" && TABLE="") == DATABASE
from
(
v
).
where
(
v
.
schema
).
is
(
""
).
and
(
v
.
table
).
is
(
""
).
selectFirst
();
if
(
dbVersion
==
null
)
{
// Database has no version registration, but model specifies
// version. Insert DbVersion entry and return.
DbVersion
newDb
=
new
DbVersion
(
model
.
version
());
insert
(
newDb
);
}
else
{
// Database has a version registration,
// check to see if upgrade is required.
if
((
model
.
version
()
>
dbVersion
.
version
)
&&
(
dbUpgrader
!=
null
))
{
// Database is an older version than model.
boolean
success
=
dbUpgrader
.
upgradeDatabase
(
this
,
dbVersion
.
version
,
model
.
version
());
if
(
success
)
{
dbVersion
.
version
=
model
.
version
();
update
(
dbVersion
);
}
}
}
}
}
return
this
;
}
<
T
>
void
upgradeTable
(
TableDefinition
<
T
>
model
)
{
if
(!
upgradeChecked
.
contains
(
model
.
getModelClass
()))
{
// Flag as checked immediately because calls are nested.
upgradeChecked
.
add
(
model
.
getModelClass
());
<
T
>
void
createTable
(
Class
<
T
>
clazz
)
{
define
(
clazz
).
createTableIfRequired
(
this
);
if
(
model
.
tableVersion
>
0
)
{
// Table is using JaQu version tracking.
DbVersion
v
=
new
DbVersion
();
String
schema
=
StringUtils
.
isNullOrEmpty
(
model
.
schemaName
)
?
""
:
model
.
schemaName
;
DbVersion
dbVersion
=
from
(
v
).
where
(
v
.
schema
).
like
(
schema
).
and
(
v
.
table
)
.
like
(
model
.
tableName
).
selectFirst
();
if
(
dbVersion
==
null
)
{
// Table has no version registration, but model specifies
// version. Insert DbVersion entry and return.
DbVersion
newTable
=
new
DbVersion
(
model
.
tableVersion
);
newTable
.
schema
=
schema
;
newTable
.
table
=
model
.
tableName
;
insert
(
newTable
);
}
else
{
// Table has a version registration.
// Check to see if upgrade is required.
if
((
model
.
tableVersion
>
dbVersion
.
version
)
&&
(
dbUpgrader
!=
null
))
{
// Table is an older version than model.
boolean
success
=
dbUpgrader
.
upgradeTable
(
this
,
schema
,
model
.
tableName
,
dbVersion
.
version
,
model
.
tableVersion
);
if
(
success
)
{
dbVersion
.
version
=
model
.
tableVersion
;
update
(
dbVersion
);
}
}
}
}
}
}
<
T
>
TableDefinition
<
T
>
define
(
Class
<
T
>
clazz
)
{
...
...
@@ -138,10 +230,32 @@ public class Db {
T
t
=
instance
(
clazz
);
Table
table
=
(
Table
)
t
;
Define
.
define
(
def
,
table
);
}
else
if
(
clazz
.
isAnnotationPresent
(
JQTable
.
class
))
{
// Annotated Class skips Define().define() static initializer
T
t
=
instance
(
clazz
);
def
.
mapObject
(
t
);
}
}
return
def
;
}
public
synchronized
void
setDbUpgrader
(
DbUpgrader
upgrader
)
{
if
(
upgrader
==
null
)
throw
new
RuntimeException
(
"DbUpgrader may not be NULL!"
);
if
(!
upgrader
.
getClass
().
isAnnotationPresent
(
JQDatabase
.
class
))
throw
new
RuntimeException
(
"DbUpgrader must be annotated with "
+
JQDatabase
.
class
.
getSimpleName
()
+
"!"
);
this
.
dbUpgrader
=
upgrader
;
upgradeChecked
.
clear
();
}
SQLDialect
getDialect
()
{
return
dialect
;
}
public
Connection
getConnection
()
{
return
conn
;
}
public
void
close
()
{
try
{
...
...
@@ -161,8 +275,30 @@ public class Db {
}
}
PreparedStatement
prepare
(
String
sql
)
{
public
<
T
>
List
<
Long
>
insertAllAndGetKeys
(
List
<
T
>
list
)
{
List
<
Long
>
identities
=
new
ArrayList
<
Long
>();
for
(
T
t
:
list
)
{
identities
.
add
(
insertAndGetKey
(
t
));
}
return
identities
;
}
public
<
T
>
void
updateAll
(
List
<
T
>
list
)
{
for
(
T
t
:
list
)
{
update
(
t
);
}
}
public
<
T
>
void
deleteAll
(
List
<
T
>
list
)
{
for
(
T
t
:
list
)
{
delete
(
t
);
}
}
PreparedStatement
prepare
(
String
sql
,
boolean
returnKey
)
{
try
{
if
(
returnKey
)
return
conn
.
prepareStatement
(
sql
,
PreparedStatement
.
RETURN_GENERATED_KEYS
);
return
conn
.
prepareStatement
(
sql
);
}
catch
(
SQLException
e
)
{
throw
new
RuntimeException
(
e
);
...
...
h2/src/tools/org/h2/jaqu/DbInspector.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: James Moger
*/
package
org
.
h2
.
jaqu
;
import
java.sql.DatabaseMetaData
;
import
java.sql.ResultSet
;
import
java.sql.SQLException
;
import
java.text.MessageFormat
;
import
java.util.ArrayList
;
import
java.util.List
;
import
org.h2.jaqu.Table.JQTable
;
import
org.h2.jaqu.util.JdbcUtils
;
import
org.h2.jaqu.util.StringUtils
;
import
org.h2.jaqu.util.Utils
;
/**
* Class to inspect a model and a database for the purposes of model validation
* and automatic model generation. This class finds the svailable schemas and
* tables and serves as the entry point for model generation and validation.
*
*/
public
class
DbInspector
{
Db
db
;
DatabaseMetaData
metadata
;
Class
<?
extends
java
.
util
.
Date
>
dateClazz
=
java
.
util
.
Date
.
class
;
public
DbInspector
(
Db
db
)
{
this
.
db
=
db
;
}
/**
* Set the preferred Date class.
* java.util.Date (default)
* java.sql.Timestamp
*
* @param dateClazz
*/
public
void
setPreferredDateClass
(
Class
<?
extends
java
.
util
.
Date
>
dateClazz
)
{
this
.
dateClazz
=
dateClazz
;
}
/**
* Generates models class skeletons for schemas and tables.
*
* @param schema (optional)
* @param table (required)
* @param packageName (optional)
* @param annotateSchema (includes schema name in annotation)
* @param trimStrings (trims strings to maxLength of column)
* @return List<String> source code models as strings
*/
public
List
<
String
>
generateModel
(
String
schema
,
String
table
,
String
packageName
,
boolean
annotateSchema
,
boolean
trimStrings
)
{
try
{
List
<
String
>
models
=
Utils
.
newArrayList
();
List
<
TableInspector
>
tables
=
findTables
(
schema
,
table
);
for
(
TableInspector
t
:
tables
)
{
t
.
read
(
metadata
);
String
model
=
t
.
generateModel
(
packageName
,
annotateSchema
,
trimStrings
);
models
.
add
(
model
);
}
return
models
;
}
catch
(
SQLException
s
)
{
throw
new
RuntimeException
(
s
);
}
}
/**
* Validates a model.
*
* @param <T> type of model
* @param model class
* @param throwOnError
* @return
*/
public
<
T
>
List
<
Validation
>
validateModel
(
T
model
,
boolean
throwOnError
)
{
try
{
TableInspector
inspector
=
findTable
(
model
);
inspector
.
read
(
metadata
);
Class
clazz
=
model
.
getClass
();
TableDefinition
<
T
>
def
=
db
.
define
(
clazz
);
return
inspector
.
validate
(
def
,
throwOnError
);
}
catch
(
SQLException
s
)
{
throw
new
RuntimeException
(
s
);
}
}
private
DatabaseMetaData
metadata
()
throws
SQLException
{
if
(
metadata
==
null
)
metadata
=
db
.
getConnection
().
getMetaData
();
return
metadata
;
}
/**
* Attempts to find a table in the database based on the model definition.
*
* @param <T>
* @param model
* @return
* @throws SQLException
*/
private
<
T
>
TableInspector
findTable
(
T
model
)
throws
SQLException
{
Class
clazz
=
model
.
getClass
();
TableDefinition
<
T
>
def
=
db
.
define
(
clazz
);
boolean
forceUpperCase
=
metadata
().
storesUpperCaseIdentifiers
();
String
sname
=
(
forceUpperCase
&&
def
.
schemaName
!=
null
)
?
def
.
schemaName
.
toUpperCase
()
:
def
.
schemaName
;
String
tname
=
forceUpperCase
?
def
.
tableName
.
toUpperCase
()
:
def
.
tableName
;
List
<
TableInspector
>
tables
=
findTables
(
sname
,
tname
);
return
tables
.
get
(
0
);
}
/**
* Returns a list of tables
*
* @param schema
* @param table
* @return
* @throws SQLException
*/
private
List
<
TableInspector
>
findTables
(
String
schema
,
String
table
)
throws
SQLException
{
ResultSet
rs
=
null
;
try
{
rs
=
metadata
().
getSchemas
();
ArrayList
<
String
>
schemaList
=
Utils
.
newArrayList
();
while
(
rs
.
next
())
schemaList
.
add
(
rs
.
getString
(
"TABLE_SCHEM"
));
JdbcUtils
.
closeSilently
(
rs
);
// Get JaQu Tables table name.
String
jaquTables
=
DbVersion
.
class
.
getAnnotation
(
JQTable
.
class
).
name
();
List
<
TableInspector
>
tables
=
Utils
.
newArrayList
();
if
(
schemaList
.
size
()
==
0
)
schemaList
.
add
(
null
);
for
(
String
s
:
schemaList
)
{
rs
=
metadata
().
getTables
(
null
,
s
,
null
,
new
String
[]
{
"TABLE"
});
while
(
rs
.
next
())
{
String
t
=
rs
.
getString
(
"TABLE_NAME"
);
if
(!
t
.
equalsIgnoreCase
(
jaquTables
))
// Ignore JaQu versions table
tables
.
add
(
new
TableInspector
(
s
,
t
,
metadata
().
storesUpperCaseIdentifiers
(),
dateClazz
));
}
}
if
(
StringUtils
.
isNullOrEmpty
(
schema
)
&&
StringUtils
.
isNullOrEmpty
(
table
))
{
// All schemas and tables
return
tables
;
}
else
{
// schema subset OR table subset OR exact match
List
<
TableInspector
>
matches
=
Utils
.
newArrayList
();
for
(
TableInspector
t
:
tables
)
{
if
(
t
.
matches
(
schema
,
table
))
matches
.
add
(
t
);
}
if
(
matches
.
size
()
==
0
)
throw
new
RuntimeException
(
MessageFormat
.
format
(
"Failed to find schema={0} table={1}"
,
schema
==
null
?
""
:
schema
,
table
==
null
?
""
:
table
));
return
matches
;
}
}
finally
{
JdbcUtils
.
closeSilently
(
rs
);
}
}
}
h2/src/tools/org/h2/jaqu/DbUpgrader.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, Version
* 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). Initial Developer: James Moger
*/
package
org
.
h2
.
jaqu
;
import
org.h2.jaqu.Table.JQDatabase
;
/**
* Interface which defines a class to handle table changes based on model
* versions.
* <p>
* An implementation of <i>DbUpgrader</i> <b>MUST</b> be annotated with the
* <i>JQDatabase</i> annotation. This annotation defines the expected database
* version number.
*
*/
public
interface
DbUpgrader
{
/**
* Defines method interface to handle database upgrades. This method is only
* called if your <i>DbUpgrader</i> implementation is annotated with
* JQDatabase.
*
* @param db
* @param fromVersion
* @param toVersion
* @return Returns <b>true</b> for successful upgrade.<br>
* If update is successful, JaQu automatically updates its version
* registry.
*/
public
boolean
upgradeDatabase
(
Db
db
,
int
fromVersion
,
int
toVersion
);
/**
* Defines method interface to handle table upgrades.
*
* @param db
* @param schema
* @param table
* @param fromVersion
* @param toVersion
* @return Returns <b>true</b> for successful upgrade.<br>
* If update is successful, JaQu automatically updates its version
* registry.
*/
public
boolean
upgradeTable
(
Db
db
,
String
schema
,
String
table
,
int
fromVersion
,
int
toVersion
);
/**
* Default Db Upgrader.
* <p>
* Does <b>NOT</b> handle upgrade requests. Instead, this throws
* RuntimeExceptions.
*/
@JQDatabase
(
version
=
0
)
public
static
class
DefaultDbUpgrader
implements
DbUpgrader
{
@Override
public
boolean
upgradeDatabase
(
Db
db
,
int
fromVersion
,
int
toVersion
)
{
throw
new
RuntimeException
(
"Please provide your own DbUpgrader implementation."
);
}
@Override
public
boolean
upgradeTable
(
Db
db
,
String
schema
,
String
table
,
int
fromVersion
,
int
toVersion
)
{
throw
new
RuntimeException
(
"Please provide your own DbUpgrader implementation."
);
}
}
}
h2/src/tools/org/h2/jaqu/DbVersion.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: James Moger
*/
package
org
.
h2
.
jaqu
;
import
org.h2.jaqu.Table.JQColumn
;
import
org.h2.jaqu.Table.JQTable
;
/**
* Model class for JaQu to track db and table versions.
*
*/
@JQTable
(
name
=
"_jq_versions"
,
primaryKey
=
"schemaName tableName"
,
memoryTable
=
true
)
public
class
DbVersion
{
@JQColumn
(
name
=
"schemaName"
,
allowNull
=
false
)
String
schema
=
""
;
@JQColumn
(
name
=
"tableName"
,
allowNull
=
false
)
String
table
=
""
;
@JQColumn
(
name
=
"version"
)
Integer
version
;
public
DbVersion
()
{
}
/**
* Constructor for defining a version entry.
* (SCHEMA="" && TABLE="") == DATABASE
*
* @param version
*/
public
DbVersion
(
int
version
)
{
this
.
schema
=
""
;
this
.
table
=
""
;
this
.
version
=
version
;
}
}
h2/src/tools/org/h2/jaqu/Declaration.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: James Moger
*/
package
org
.
h2
.
jaqu
;
/**
* Classes implementing this interface can be used as a declaration in a statement.
*/
public
interface
Declaration
{
/**
* Append the SQL to the given statement using the given query.
*
* @param stat the statement to append the SQL to
*/
//## Java 1.5 begin ##
void
appendSQL
(
SQLStatement
stat
);
//## Java 1.5 end ##
}
h2/src/tools/org/h2/jaqu/Define.java
浏览文件 @
0bedabe6
...
...
@@ -6,6 +6,8 @@
*/
package
org
.
h2
.
jaqu
;
import
org.h2.jaqu.Table.IndexType
;
/**
* This class provides utility methods to define primary keys, indexes, and set
* the name of the table.
...
...
@@ -23,7 +25,22 @@ public class Define {
public
static
void
index
(
Object
...
columns
)
{
checkInDefine
();
currentTableDefinition
.
addIndex
(
columns
);
currentTableDefinition
.
addIndex
(
IndexType
.
STANDARD
,
columns
);
}
public
static
void
uniqueIndex
(
Object
...
columns
)
{
checkInDefine
();
currentTableDefinition
.
addIndex
(
IndexType
.
UNIQUE
,
columns
);
}
public
static
void
hashIndex
(
Object
column
)
{
checkInDefine
();
currentTableDefinition
.
addIndex
(
IndexType
.
HASH
,
new
Object
[]
{
column
});
}
public
static
void
uniqueHashIndex
(
Object
column
)
{
checkInDefine
();
currentTableDefinition
.
addIndex
(
IndexType
.
UNIQUE_HASH
,
new
Object
[]
{
column
});
}
public
static
void
maxLength
(
Object
column
,
int
length
)
{
...
...
h2/src/tools/org/h2/jaqu/IncrementColumn.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: James Moger
*/
package
org
.
h2
.
jaqu
;
/**
* This class represents a "column=(column + 1)" token for a SET statement.
*
* @param <A> the new value data type
*/
//## Java 1.5 begin ##
public
class
IncrementColumn
<
T
,
A
>
implements
Declaration
{
private
Query
<
T
>
query
;
private
A
x
;
private
A
y
;
IncrementColumn
(
Query
<
T
>
query
,
A
x
)
{
this
.
query
=
query
;
this
.
x
=
x
;
}
public
Query
<
T
>
by
(
A
y
)
{
query
.
addDeclarationToken
(
this
);
this
.
y
=
y
;
return
query
;
}
@Override
public
void
appendSQL
(
SQLStatement
stat
)
{
query
.
appendSQL
(
stat
,
x
);
stat
.
appendSQL
(
"=("
);
query
.
appendSQL
(
stat
,
x
);
if
(
y
instanceof
Number
)
{
Number
n
=
(
Number
)
y
;
if
(
n
.
doubleValue
()
>
0
)
stat
.
appendSQL
(
"+"
);
}
stat
.
appendSQL
(
y
.
toString
());
stat
.
appendSQL
(
")"
);
}
}
//## Java 1.5 end ##
h2/src/tools/org/h2/jaqu/ModelUtils.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: James Moger
*/
package
org
.
h2
.
jaqu
;
import
static
org
.
h2
.
jaqu
.
util
.
StringUtils
.
isNullOrEmpty
;
import
java.lang.reflect.Method
;
import
java.math.BigDecimal
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.regex.Pattern
;
import
org.h2.jaqu.TableDefinition.FieldDefinition
;
/**
* Utility methods for models related to type mapping, default value validation,
* and class or field name creation.
*/
public
class
ModelUtils
{
/**
* Returns a SQL type mapping for a Java class.
*
* @param field the field to map
* @param strictTypeMapping throws a RuntimeException if type is unsupported
* @return
*/
public
static
String
getDataType
(
FieldDefinition
fieldDef
,
boolean
strictTypeMapping
)
{
Class
<?>
fieldClass
=
fieldDef
.
field
.
getType
();
if
(
supportedTypes
.
containsKey
(
fieldClass
))
{
String
sqltype
=
supportedTypes
.
get
(
fieldClass
);
if
(
sqltype
.
equals
(
"VARCHAR"
)
&&
fieldDef
.
maxLength
<=
0
)
// Unspecified length strings are TEXT, not VARCHAR
return
"TEXT"
;
return
sqltype
;
}
if
(!
strictTypeMapping
)
return
"VARCHAR"
;
else
throw
new
RuntimeException
(
"Unsupported type "
+
fieldClass
.
getName
());
}
@SuppressWarnings
(
"serial"
)
// Used by Runtime Mapping for CREATE statements
static
Map
<
Class
<?>,
String
>
supportedTypes
=
new
HashMap
<
Class
<?>,
String
>()
{
{
put
(
String
.
class
,
"VARCHAR"
);
put
(
Boolean
.
class
,
"BIT"
);
put
(
Byte
.
class
,
"TINYINT"
);
put
(
Short
.
class
,
"SMALLINT"
);
put
(
Integer
.
class
,
"INT"
);
put
(
Long
.
class
,
"BIGINT"
);
put
(
Float
.
class
,
"REAL"
);
put
(
Double
.
class
,
"DOUBLE"
);
put
(
BigDecimal
.
class
,
"DECIMAL"
);
put
(
java
.
sql
.
Timestamp
.
class
,
"TIMESTAMP"
);
put
(
java
.
util
.
Date
.
class
,
"TIMESTAMP"
);
put
(
java
.
sql
.
Date
.
class
,
"DATE"
);
put
(
java
.
sql
.
Time
.
class
,
"TIME"
);
// TODO add blobs, binary types, custom types?
}
};
/**
* Returns the Java class type for a given SQL type.
*
* @param sqlType
* @param dateClazz the preferred date class (java.util.Date or java.sql.Timestamp)
* @return
*/
public
static
Class
<?>
getClassType
(
String
sqlType
,
Class
<?
extends
java
.
util
.
Date
>
dateClazz
)
{
sqlType
=
sqlType
.
toUpperCase
();
// FIXME dropping "UNSIGNED" or parts like that. could be trouble.
sqlType
=
sqlType
.
split
(
" "
)[
0
].
trim
();
if
(
sqlTypes
.
containsKey
(
sqlType
))
// Marshall sqlType to a standard type
sqlType
=
sqlTypes
.
get
(
sqlType
);
Class
<?>
mappedClazz
=
null
;
for
(
Class
<?>
clazz
:
supportedTypes
.
keySet
())
if
(
supportedTypes
.
get
(
clazz
).
equalsIgnoreCase
(
sqlType
))
{
mappedClazz
=
clazz
;
break
;
}
if
(
mappedClazz
!=
null
)
{
if
(
mappedClazz
.
equals
(
java
.
util
.
Date
.
class
)
||
mappedClazz
.
equals
(
java
.
sql
.
Timestamp
.
class
))
return
dateClazz
;
return
mappedClazz
;
}
return
null
;
}
// Marshall SQL type aliases to the list of supported types.
// Used by Generation and Validation
static
Map
<
String
,
String
>
sqlTypes
=
new
HashMap
<
String
,
String
>()
{
{
// Strings
put
(
"CHAR"
,
"VARCHAR"
);
put
(
"CHARACTER"
,
"VARCHAR"
);
put
(
"NCHAR"
,
"VARCHAR"
);
put
(
"VARCHAR_CASESENSITIVE"
,
"VARCHAR"
);
put
(
"VARCHAR_IGNORECASE"
,
"VARCHAR"
);
put
(
"LONGVARCHAR"
,
"VARCHAR"
);
put
(
"VARCHAR2"
,
"VARCHAR"
);
put
(
"NVARCHAR"
,
"VARCHAR"
);
put
(
"NVARCHAR2"
,
"VARCHAR"
);
put
(
"TEXT"
,
"VARCHAR"
);
put
(
"NTEXT"
,
"VARCHAR"
);
put
(
"TINYTEXT"
,
"VARCHAR"
);
put
(
"MEDIUMTEXT"
,
"VARCHAR"
);
put
(
"LONGTEXT"
,
"VARCHAR"
);
put
(
"CLOB"
,
"VARCHAR"
);
put
(
"NCLOB"
,
"VARCHAR"
);
// Logic
put
(
"BOOL"
,
"BIT"
);
put
(
"BOOLEAN"
,
"BIT"
);
// Whole Numbers
put
(
"BYTE"
,
"TINYINT"
);
put
(
"INT2"
,
"SMALLINT"
);
put
(
"YEAR"
,
"SMALLINT"
);
put
(
"INTEGER"
,
"INT"
);
put
(
"MEDIUMINT"
,
"INT"
);
put
(
"INT4"
,
"INT"
);
put
(
"SIGNED"
,
"INT"
);
put
(
"INT8"
,
"BIGINT"
);
put
(
"IDENTITY"
,
"BIGINT"
);
// Decimals
put
(
"NUMBER"
,
"DECIMAL"
);
put
(
"DEC"
,
"DECIMAL"
);
put
(
"NUMERIC"
,
"DECIMAL"
);
put
(
"FLOAT"
,
"DOUBLE"
);
put
(
"FLOAT4"
,
"DOUBLE"
);
put
(
"FLOAT8"
,
"DOUBLE"
);
// Dates
put
(
"DATETIME"
,
"TIMESTAMP"
);
put
(
"SMALLDATETIME"
,
"TIMESTAMP"
);
}
};
/**
* Tries to create a CamelCase class name from a table.
*
* @param name
* @return
*/
public
static
String
createClassName
(
String
name
)
{
String
[]
chunks
=
name
.
split
(
"_"
);
StringBuilder
newName
=
new
StringBuilder
();
for
(
String
chunk
:
chunks
)
{
if
(
chunk
.
length
()
==
0
)
// leading or trailing _
continue
;
newName
.
append
(
Character
.
toUpperCase
(
chunk
.
charAt
(
0
)));
newName
.
append
(
chunk
.
substring
(
1
).
toLowerCase
());
}
return
newName
.
toString
();
}
/**
* Ensures that table column names don't collide with Java keywords.
*
* @param col
* @return
*/
public
static
String
createFieldName
(
String
col
)
{
String
cn
=
col
.
toLowerCase
();
if
(
keywords
.
contains
(
cn
))
cn
+=
"_value"
;
return
cn
;
}
@SuppressWarnings
(
"serial"
)
static
List
<
String
>
keywords
=
new
ArrayList
<
String
>()
{
{
add
(
"abstract"
);
add
(
"assert"
);
add
(
"boolean"
);
add
(
"break"
);
add
(
"byte"
);
add
(
"case"
);
add
(
"catch"
);
add
(
"char"
);
add
(
"class"
);
add
(
"const"
);
add
(
"continue"
);
add
(
"default"
);
add
(
"do"
);
add
(
"double"
);
add
(
"else"
);
add
(
"enum"
);
add
(
"extends"
);
add
(
"final"
);
add
(
"finally"
);
add
(
"float"
);
add
(
"for"
);
add
(
"goto"
);
add
(
"if"
);
add
(
"implements"
);
add
(
"import"
);
add
(
"instanceof"
);
add
(
"int"
);
add
(
"interface"
);
add
(
"long"
);
add
(
"native"
);
add
(
"new"
);
add
(
"package"
);
add
(
"private"
);
add
(
"protected"
);
add
(
"public"
);
add
(
"return"
);
add
(
"short"
);
add
(
"static"
);
add
(
"strictfp"
);
add
(
"super"
);
add
(
"switch"
);
add
(
"synchronized"
);
add
(
"this"
);
add
(
"throw"
);
add
(
"throws"
);
add
(
"transient"
);
add
(
"try"
);
add
(
"void"
);
add
(
"volatile"
);
add
(
"while"
);
add
(
"false"
);
add
(
"null"
);
add
(
"true"
);
}
};
/**
* Checks the formatting of JQColumn.defaultValue()
* @param defaultValue
* @return
*/
public
static
boolean
isProperlyFormattedDefaultValue
(
String
defaultValue
)
{
if
(
isNullOrEmpty
(
defaultValue
))
return
true
;
Pattern
literalDefault
=
Pattern
.
compile
(
"'.*'"
);
Pattern
functionDefault
=
Pattern
.
compile
(
"[^'].*[^']"
);
return
literalDefault
.
matcher
(
defaultValue
).
matches
()
||
functionDefault
.
matcher
(
defaultValue
).
matches
();
}
/**
* Checks to see if the defaultValue matches the Class.
*
* @param modelClazz
* @param defaultValue
* @return
*/
public
static
boolean
isValidDefaultValue
(
Class
<?>
modelClazz
,
String
defaultValue
)
{
if
(
defaultValue
==
null
)
// NULL
return
true
;
if
(
defaultValue
.
trim
().
length
()
==
0
)
// NULL (effectively)
return
true
;
// FIXME H2 single-quotes literal values. Very Useful.
// MySQL does not single-quote literal values so its hard to
// differentiate a FUNCTION/VARIABLE from a literal value.
// Function/Variable
Pattern
functionDefault
=
Pattern
.
compile
(
"[^'].*[^']"
);
if
(
functionDefault
.
matcher
(
defaultValue
).
matches
())
// Hard to validate this since its in the DB. Assume its good.
return
true
;
// STRING
if
(
modelClazz
==
String
.
class
)
{
Pattern
stringDefault
=
Pattern
.
compile
(
"'(.|\\n)*'"
);
return
stringDefault
.
matcher
(
defaultValue
).
matches
();
}
String
dateRegex
=
"[0-9]{1,4}[-/\\.][0-9]{1,2}[-/\\.][0-9]{1,2}"
;
String
timeRegex
=
"[0-2]{1}[0-9]{1}:[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}"
;
// TIMESTAMPs
if
(
modelClazz
==
java
.
util
.
Date
.
class
||
modelClazz
==
java
.
sql
.
Timestamp
.
class
){
// This may be a little loose....
// 00-00-00 00:00:00
// 00/00/00T00:00:00
// 00.00.00T00:00:00
Pattern
pattern
=
Pattern
.
compile
(
"'"
+
dateRegex
+
"."
+
timeRegex
+
"'"
);
return
pattern
.
matcher
(
defaultValue
).
matches
();
}
// DATE
if
(
modelClazz
==
java
.
sql
.
Date
.
class
)
{
// This may be a little loose....
// 00-00-00
// 00/00/00
// 00.00.00
Pattern
pattern
=
Pattern
.
compile
(
"'"
+
dateRegex
+
"'"
);
return
pattern
.
matcher
(
defaultValue
).
matches
();
}
// TIME
if
(
modelClazz
==
java
.
sql
.
Time
.
class
)
{
// 00:00:00
Pattern
pattern
=
Pattern
.
compile
(
"'"
+
timeRegex
+
"'"
);
return
pattern
.
matcher
(
defaultValue
).
matches
();
}
// NUMBER
if
(
Number
.
class
.
isAssignableFrom
(
modelClazz
))
{
// Strip single quotes
String
unquoted
=
defaultValue
;
if
(
unquoted
.
charAt
(
0
)
==
'\''
)
unquoted
=
unquoted
.
substring
(
1
);
if
(
unquoted
.
charAt
(
unquoted
.
length
()
-
1
)
==
'\''
)
unquoted
=
unquoted
.
substring
(
0
,
unquoted
.
length
()
-
1
);
try
{
// Delegate to static valueOf() method to parse string
Method
m
=
modelClazz
.
getMethod
(
"valueOf"
,
String
.
class
);
Object
o
=
m
.
invoke
(
null
,
unquoted
);
}
catch
(
NumberFormatException
nex
)
{
return
false
;
}
catch
(
Throwable
t
)
{
}
}
return
true
;
}
}
h2/src/tools/org/h2/jaqu/Query.java
浏览文件 @
0bedabe6
...
...
@@ -8,6 +8,7 @@ package org.h2.jaqu;
//## Java 1.5 begin ##
import
java.lang.reflect.Field
;
import
java.sql.Clob
;
import
java.sql.ResultSet
;
import
java.sql.SQLException
;
import
java.util.ArrayList
;
...
...
@@ -15,9 +16,10 @@ import java.util.HashMap;
import
java.util.IdentityHashMap
;
import
java.util.List
;
import
org.h2.jaqu.bytecode.ClassReader
;
import
org.h2.jaqu.util.StatementLogger
;
import
org.h2.jaqu.util.JdbcUtils
;
import
org.h2.jaqu.util.Utils
;
import
org.h2.util.JdbcUtils
;
import
org.h2.util.New
;
//import org.h2.util.JdbcUtils;
//## Java 1.5 end ##
/**
...
...
@@ -31,10 +33,13 @@ public class Query<T> {
private
Db
db
;
private
SelectTable
<
T
>
from
;
private
ArrayList
<
Token
>
conditions
=
Utils
.
newArrayList
();
private
ArrayList
<
Declaration
>
declarations
=
Utils
.
newArrayList
();
private
ArrayList
<
SelectTable
<?>>
joins
=
Utils
.
newArrayList
();
private
final
IdentityHashMap
<
Object
,
SelectColumn
<
T
>>
aliasMap
=
Utils
.
newIdentityHashMap
();
private
ArrayList
<
OrderExpression
<
T
>>
orderByList
=
Utils
.
newArrayList
();
private
Object
[]
groupByExpressions
;
private
long
limit
=
0
;
private
long
offset
=
0
;
Query
(
Db
db
)
{
this
.
db
=
db
;
...
...
@@ -60,7 +65,7 @@ public class Query<T> {
}
catch
(
SQLException
e
)
{
throw
new
RuntimeException
(
e
);
}
finally
{
JdbcUtils
.
closeSilently
(
rs
);
JdbcUtils
.
closeSilently
(
rs
,
true
);
}
}
...
...
@@ -102,7 +107,7 @@ public class Query<T> {
}
catch
(
SQLException
e
)
{
throw
new
RuntimeException
(
e
);
}
finally
{
JdbcUtils
.
closeSilently
(
rs
);
JdbcUtils
.
closeSilently
(
rs
,
true
);
}
return
result
;
}
...
...
@@ -112,8 +117,36 @@ public class Query<T> {
stat
.
appendSQL
(
"DELETE FROM "
);
from
.
appendSQL
(
stat
);
appendWhere
(
stat
);
StatementLogger
.
delete
(
stat
.
getSQL
());
return
stat
.
executeUpdate
();
}
public
<
A
>
SetColumn
<
T
,
A
>
set
(
A
field
)
{
return
new
SetColumn
<
T
,
A
>(
this
,
field
);
}
public
<
A
>
IncrementColumn
<
T
,
A
>
increment
(
A
field
)
{
return
new
IncrementColumn
<
T
,
A
>(
this
,
field
);
}
public
int
update
()
{
if
(
declarations
.
size
()
==
0
)
throw
new
RuntimeException
(
"Please specify SET or INCREMENT before calling Update!"
);
SQLStatement
stat
=
new
SQLStatement
(
db
);
stat
.
appendSQL
(
"UPDATE "
);
from
.
appendSQL
(
stat
);
stat
.
appendSQL
(
" SET "
);
int
i
=
0
;
for
(
Declaration
declaration:
declarations
)
{
if
(
i
++
>
0
)
{
stat
.
appendSQL
(
", "
);
}
declaration
.
appendSQL
(
stat
);
}
appendWhere
(
stat
);
StatementLogger
.
update
(
stat
.
getSQL
());
return
stat
.
executeUpdate
();
}
public
<
X
,
Z
>
List
<
X
>
selectDistinct
(
Z
x
)
{
return
select
(
x
,
true
);
...
...
@@ -147,7 +180,7 @@ public class Query<T> {
}
catch
(
SQLException
e
)
{
throw
new
RuntimeException
(
e
);
}
finally
{
JdbcUtils
.
closeSilently
(
rs
);
JdbcUtils
.
closeSilently
(
rs
,
true
);
}
return
result
;
}
...
...
@@ -161,7 +194,12 @@ public class Query<T> {
try
{
while
(
rs
.
next
())
{
try
{
X
value
=
(
X
)
rs
.
getObject
(
1
);
X
value
;
Object
o
=
rs
.
getObject
(
1
);
if
(
Clob
.
class
.
isAssignableFrom
(
o
.
getClass
()))
{
value
=
(
X
)
Utils
.
convert
(
o
,
String
.
class
);
}
else
value
=
(
X
)
o
;
result
.
add
(
value
);
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
);
...
...
@@ -170,7 +208,7 @@ public class Query<T> {
}
catch
(
SQLException
e
)
{
throw
new
RuntimeException
(
e
);
}
finally
{
JdbcUtils
.
closeSilently
(
rs
);
JdbcUtils
.
closeSilently
(
rs
,
true
);
}
return
result
;
}
...
...
@@ -180,7 +218,7 @@ public class Query<T> {
}
public
<
A
>
QueryWhere
<
T
>
where
(
Filter
filter
)
{
HashMap
<
String
,
Object
>
fieldMap
=
New
.
h
ashMap
();
HashMap
<
String
,
Object
>
fieldMap
=
Utils
.
newH
ashMap
();
for
(
Field
f
:
filter
.
getClass
().
getDeclaredFields
())
{
f
.
setAccessible
(
true
);
try
{
...
...
@@ -212,6 +250,23 @@ public class Query<T> {
}
//## Java 1.5 end ##
/**
* Sets the Limit and Offset of a query.
*
* @return the query
*/
//## Java 1.5 begin ##
public
Query
<
T
>
limit
(
long
limit
)
{
this
.
limit
=
limit
;
return
this
;
}
public
Query
<
T
>
offset
(
long
offset
)
{
this
.
offset
=
offset
;
return
this
;
}
//## Java 1.5 end ##
/**
* Order by a number of columns.
*
...
...
@@ -268,6 +323,10 @@ public class Query<T> {
void
addConditionToken
(
Token
condition
)
{
conditions
.
add
(
condition
);
}
void
addDeclarationToken
(
Declaration
declaration
)
{
declarations
.
add
(
declaration
);
}
void
appendWhere
(
SQLStatement
stat
)
{
if
(!
conditions
.
isEmpty
())
{
...
...
@@ -317,6 +376,11 @@ public class Query<T> {
stat
.
appendSQL
(
" "
);
}
}
if
(
limit
>
0
)
db
.
getDialect
().
appendLimit
(
stat
,
limit
);
if
(
offset
>
0
)
db
.
getDialect
().
appendOffset
(
stat
,
offset
);
StatementLogger
.
select
(
stat
.
getSQL
());
return
stat
;
}
//## Java 1.5 end ##
...
...
h2/src/tools/org/h2/jaqu/QueryWhere.java
浏览文件 @
0bedabe6
...
...
@@ -33,6 +33,16 @@ public class QueryWhere<T> {
query
.
addConditionToken
(
ConditionAndOr
.
OR
);
return
new
QueryCondition
<
T
,
A
>(
query
,
x
);
}
public
QueryWhere
<
T
>
limit
(
long
limit
)
{
query
.
limit
(
limit
);
return
this
;
}
public
QueryWhere
<
T
>
offset
(
long
offset
)
{
query
.
offset
(
offset
);
return
this
;
}
public
<
X
,
Z
>
List
<
X
>
select
(
Z
x
)
{
return
query
.
select
(
x
);
...
...
@@ -123,6 +133,10 @@ public class QueryWhere<T> {
return
query
.
delete
();
}
public
int
update
()
{
return
query
.
update
();
}
public
long
selectCount
()
{
return
query
.
selectCount
();
}
...
...
h2/src/tools/org/h2/jaqu/SQLDialect.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: James Moger
*/
package
org
.
h2
.
jaqu
;
import
java.text.MessageFormat
;
import
org.h2.jaqu.TableDefinition.IndexDefinition
;
import
org.h2.jaqu.util.StatementBuilder
;
import
org.h2.jaqu.util.StringUtils
;
/**
* Interface that defines points where JaQu can build different statements for
* DB-specific SQL.
*
*/
public
interface
SQLDialect
{
public
String
tableName
(
String
schema
,
String
table
);
public
String
createIndex
(
String
schema
,
String
table
,
IndexDefinition
index
);
public
void
appendLimit
(
SQLStatement
stat
,
long
limit
);
public
void
appendOffset
(
SQLStatement
stat
,
long
offset
);
/**
* Default implementation of an SQL dialect.
* Designed for an H2 database. May be suitable for others.
*/
public
static
class
DefaultSQLDialect
implements
SQLDialect
{
@Override
public
String
tableName
(
String
schema
,
String
table
)
{
if
(
StringUtils
.
isNullOrEmpty
(
schema
))
return
table
;
return
schema
+
"."
+
table
;
}
@Override
public
String
createIndex
(
String
schema
,
String
table
,
IndexDefinition
index
)
{
StatementBuilder
cols
=
new
StatementBuilder
();
for
(
String
col:
index
.
columnNames
)
{
cols
.
appendExceptFirst
(
", "
);
cols
.
append
(
col
);
}
String
type
;
switch
(
index
.
type
)
{
case
UNIQUE:
type
=
" UNIQUE "
;
break
;
case
HASH:
type
=
" HASH "
;
break
;
case
UNIQUE_HASH:
type
=
" UNIQUE HASH "
;
break
;
case
STANDARD:
default
:
type
=
" "
;
break
;
}
return
MessageFormat
.
format
(
"CREATE{0}INDEX IF NOT EXISTS {1} ON {2}({3})"
,
type
,
index
.
indexName
,
table
,
cols
);
}
@Override
public
void
appendLimit
(
SQLStatement
stat
,
long
limit
)
{
stat
.
appendSQL
(
" LIMIT "
+
limit
);
}
@Override
public
void
appendOffset
(
SQLStatement
stat
,
long
offset
)
{
stat
.
appendSQL
(
" OFFSET "
+
offset
);
}
}
}
h2/src/tools/org/h2/jaqu/SQLStatement.java
浏览文件 @
0bedabe6
...
...
@@ -11,6 +11,7 @@ import java.sql.PreparedStatement;
import
java.sql.ResultSet
;
import
java.sql.SQLException
;
import
java.util.ArrayList
;
import
org.h2.jaqu.util.JdbcUtils
;
//## Java 1.5 end ##
/**
...
...
@@ -37,6 +38,10 @@ public class SQLStatement {
sql
=
null
;
return
this
;
}
public
SQLStatement
appendTable
(
String
schema
,
String
table
)
{
return
appendSQL
(
db
.
getDialect
().
tableName
(
schema
,
table
));
}
String
getSQL
()
{
if
(
sql
==
null
)
{
...
...
@@ -49,20 +54,42 @@ public class SQLStatement {
params
.
add
(
o
);
return
this
;
}
ResultSet
executeQuery
()
{
try
{
return
prepare
().
executeQuery
();
return
prepare
(
false
).
executeQuery
();
}
catch
(
SQLException
e
)
{
throw
new
RuntimeException
(
e
);
}
}
int
executeUpdate
()
{
PreparedStatement
ps
=
null
;
try
{
ps
=
prepare
(
false
);
return
ps
.
executeUpdate
();
}
catch
(
SQLException
e
)
{
throw
new
RuntimeException
(
e
);
}
finally
{
JdbcUtils
.
closeSilently
(
ps
);
}
}
long
executeInsert
()
{
PreparedStatement
ps
=
null
;
try
{
return
prepare
().
executeUpdate
();
ps
=
prepare
(
true
);
ps
.
executeUpdate
();
long
identity
=
-
1
;
ResultSet
rs
=
ps
.
getGeneratedKeys
();
if
(
rs
!=
null
&&
rs
.
next
())
identity
=
rs
.
getLong
(
1
);
JdbcUtils
.
closeSilently
(
rs
);
return
identity
;
}
catch
(
SQLException
e
)
{
throw
new
RuntimeException
(
e
);
}
finally
{
JdbcUtils
.
closeSilently
(
ps
);
}
}
...
...
@@ -74,8 +101,8 @@ public class SQLStatement {
}
}
private
PreparedStatement
prepare
()
{
PreparedStatement
prep
=
db
.
prepare
(
getSQL
());
private
PreparedStatement
prepare
(
boolean
returnIdentity
)
{
PreparedStatement
prep
=
db
.
prepare
(
getSQL
()
,
returnIdentity
);
for
(
int
i
=
0
;
i
<
params
.
size
();
i
++)
{
Object
o
=
params
.
get
(
i
);
setValue
(
prep
,
i
+
1
,
o
);
...
...
h2/src/tools/org/h2/jaqu/SelectTable.java
浏览文件 @
0bedabe6
...
...
@@ -55,9 +55,9 @@ class SelectTable <T> {
void
appendSQL
(
SQLStatement
stat
)
{
if
(
query
.
isJoin
())
{
stat
.
append
SQL
(
aliasDef
.
tableName
+
" AS "
+
as
);
stat
.
append
Table
(
aliasDef
.
schemaName
,
aliasDef
.
tableName
).
appendSQL
(
" AS "
+
as
);
}
else
{
stat
.
append
SQL
(
aliasDef
.
tableName
);
stat
.
append
Table
(
aliasDef
.
schemaName
,
aliasDef
.
tableName
);
}
}
...
...
h2/src/tools/org/h2/jaqu/SetColumn.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: James Moger
*/
package
org
.
h2
.
jaqu
;
/**
* This class represents a "column=value" token for a SET statement.
*
* @param <A> the new value data type
*/
//## Java 1.5 begin ##
public
class
SetColumn
<
T
,
A
>
implements
Declaration
{
private
Query
<
T
>
query
;
private
A
x
;
private
A
y
;
SetColumn
(
Query
<
T
>
query
,
A
x
)
{
this
.
query
=
query
;
this
.
x
=
x
;
}
public
Query
<
T
>
to
(
A
y
)
{
query
.
addDeclarationToken
(
this
);
this
.
y
=
y
;
return
query
;
}
@Override
public
void
appendSQL
(
SQLStatement
stat
)
{
query
.
appendSQL
(
stat
,
x
);
stat
.
appendSQL
(
"=?"
);
stat
.
addParameter
(
y
);
}
}
//## Java 1.5 end ##
h2/src/tools/org/h2/jaqu/Table.java
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, Version
* 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). Initial Developer: H2 Group
*/
package
org
.
h2
.
jaqu
;
import
java.lang.annotation.ElementType
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
/**
* A class that implements this interface can be used as a database table.
* A class that implements the JaQu model mapping options.
* <p>
* You may implement the Table interface on your model object and optionally use
* JQColumn annotations.<br>
* <i>This imposes a compile-time and runtime-dependency on JaQu.</i>
* <p>
* <u>OR</u>
* <p>
* You may choose to use the JQTable and JQColumn annotations only.<br>
* <i>This imposes a compile-time and runtime-dependency on this file only.</i>
* <p>
* <b>NOTE</b><br>
* Classes that are annotated with JQTable <b>and</b> implement Table will NOT
* call the define() method.
* <p>
* <b>Supported Data Types</b>
* <table>
* <tr>
* <td>java.lang.String</td>
* <td>VARCHAR (maxLength > 0) / TEXT (maxLength == 0)</td>
* </tr>
* <tr>
* <td>java.lang.Boolean</td>
* <td>BIT</td>
* </tr>
* <tr>
* <td>java.lang.Byte</td>
* <td>TINYINT</td>
* </tr>
* <tr>
* <td>java.lang.Short</td>
* <td>SMALLINT</td>
* </tr>
* <tr>
* <td>java.lang.Integer</td>
* <td>INT</td>
* </tr>
* <tr>
* <td>java.lang.Long</td>
* <td>BIGINT</td>
* </tr>
* <tr>
* <td>java.lang.Float</td>
* <td>REAL</td>
* </tr>
* <tr>
* <td>java.lang.Double</td>
* <td>DOUBLE</td>
* </tr>
* <tr>
* <td>java.math.BigDecimal</td>
* <td>DECIMAL</td>
* </tr>
* <tr>
* <td>java.util.Date</td>
* <td>TIMESTAMP</td>
* </tr>
* <tr>
* <td>java.sql.Date</td>
* <td>DATE</td>
* </tr>
* <tr>
* <td>java.sql.Time</td>
* <td>TIME</td>
* </tr>
* <tr>
* <td>java.sql.Timestamp</td>
* <td>TIMESTAMP</td>
* </tr>
* </table>
* <p>
* <b>Unsupported Data Types</b>
* <ul>
* <li>Binary types (BLOB, etc)
* <li>Custom types
* </ul>
* <p>
* <b>Table and Field Mapping</b>
* <p>
* By default, the mapped table name is the class name and the <i>public</i>
* fields are reflectively mapped, by their name, to columns.
* <p>
* As an alternative, you may specify both the table and column definition by
* annotations.
* <p>
* <b>Table Interface</b>
* <p>
* You may set additional parameters such as table name, primary key, and
* indexes in the <i>define()</i> method.
* <p>
* <b>Annotations</b>
* <p>
* You may use the annotations with or without implementing the Table interface.
* <br>
* The annotations allow you to decouple your model completely from JaQu other
* than this file.
* <p>
* <b>Automatic Model Generation</b>
* <p>
* You may automatically generate model classes as strings with the <i>Db</i>
* and <i>DbInspector</i> objects.
*
* <pre>
* Db db = Db.open("jdbc:h2:mem:", "sa", "sa");
* DbInspector inspector = new DbInspector(db);
* List<String> models = inspector.generateModel(schema, table, packageName,
* annotateSchema, trimStrings)
* </pre>
*
* OR you may use the <i>GenerateModels</i> tool to generate and save your
* classes to the filesystem.
*
* <pre>
* java -cp h2jaqu.jar org.h2.jaqu.util.GenerateModels
* -url "jdbc:h2:mem:"
* -user sa -password sa -schema schemaName -table tableName
* -package packageName -folder destination
* -annotateSchema false -trimStrings true
* </pre>
*
* <b>Model Validation</b>
* <p>
* You may validate your model class with <i>DbInspector</i> object.<br>
* The DbInspector will report ERRORS, WARNINGS, and SUGGESTIONS to help you.
*
* <pre>
* Db db = Db.open("jdbc:h2:mem:", "sa", "sa");
* DbInspector inspector = new DbInspector(db);
* List<Validation> remarks = inspector.validateModel(new MyModel(), throwOnError);
* for (Validation remark : remarks)
* System.out.println(remark);
* </pre>
*/
public
interface
Table
{
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
(
ElementType
.
TYPE
)
public
@interface
JQDatabase
{
/**
* If <b>version</b> is set to a <i>non-zero</i> value, JaQu will
* maintain a "_jq_versions" table within your database. The
* <i>version</i> number will be used to call to a registered
* <i>DbUpgrader</i> implementation to perform relevant ALTERs or
* whatever.
* <p>
* <b>Default: <i>0</i></b>
* <p>
* <b>NOTE:</b><br>
* You must specify a <i>DbUpgrader</i> on your <i>Db</i> object to
* use this parameter.
*/
int
version
()
default
0
;
}
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
(
ElementType
.
TYPE
)
public
@interface
JQSchema
{
/**
* <b>schema</b> may be optionally specified. If it is not specified the
* schema will be ignored.
* <p>
* <b>Default: <i>Unspecified</i></b>
*/
String
name
()
default
""
;
}
/**
* Enumeration defining the 4 index types.
*
*/
public
static
enum
IndexType
{
STANDARD
,
UNIQUE
,
HASH
,
UNIQUE_HASH
;
}
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
(
ElementType
.
TYPE
)
public
@interface
JQIndex
{
/**
* <b>standard</b> indexes may be optionally specified. If not specified,
* these values will be ignored.
* <ul>
* <li>standard = "id, name"
* <li>standard = "id name"
* <li>standard = { "id name", "date" }
* </ul>
* Standard indexes may still be added in the <i>define()</i> method if
* the model class is not annotated with JQTable.
* <p>
* <b>Default: <i>Unspecified</i></b>
*/
String
[]
standard
()
default
{};
/**
* <b>unique</b> indexes may be optionally specified. If not specified,
* these values will be ignored.
* <ul>
* <li>unique = "id, name"
* <li>unique = "id name"
* <li>unique = { "id name", "date" }
* </ul>
* Unique indexes may still be added in the <i>define()</i> method if
* the model class is not annotated with JQTable.
* <p>
* <b>Default: <i>Unspecified</i></b>
*/
String
[]
unique
()
default
{};
/**
* <b>hash</b> indexes may be optionally specified. If not specified,
* these values will be ignored.
* <ul>
* <li>hash = "name"
* <li>hash = { "name", "date" }
* </ul>
* Hash indexes may still be added in the <i>define()</i> method if
* the model class is not annotated with JQTable.
* <p>
* <b>Default: <i>Unspecified</i></b>
*/
String
[]
hash
()
default
{};
/**
* <b>uniqueHash</b> indexes may be optionally specified. If not specified,
* these values will be ignored.
* <ul>
* <li>uniqueHash = "id"
* <li>uniqueHash = "name"
* <li>uniqueHash = { "id", "name" }
* </ul>
* UniqueHash indexes may still be added in the <i>define()</i> method if
* the model class is not annotated with JQTable.
* <p>
* <b>Default: <i>Unspecified</i></b>
*/
String
[]
uniqueHash
()
default
{};
}
/**
* Annotation to define a table.
*/
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
(
ElementType
.
TYPE
)
public
@interface
JQTable
{
/**
* <b>name</b> may be optionally specified. If it is not specified the
* class name will be used as the table name.
* <p>
* The table name may still be overridden in the <i>define()</i> method
* if the model class is not annotated with JQTable.
* <p>
* <b>Default: <i>Unspecified</i></b>
*/
String
name
()
default
""
;
/**
* <b>primaryKey</b> may be optionally specified. If it is not
* specified, then no primary key will be set by the JQTable annotation.
* You may specify a composite primary key.
* <ul>
* <li>primaryKey = "id, name"
* <li>primaryKey = "id name"
* </ul>
* The primaryKey may still be overridden in the <i>define()</i> method
* if the model class is not annotated with JQTable.
* <p>
* <b>Default: <i>Unspecified</i></b>
*/
String
primaryKey
()
default
""
;
/**
* <b>inheritColumns</b> allows this model class to inherit columns from
* its super class. Any JQTable annotation present on the super class is
* ignored.<br>
* <p>
* <b>Default: <i>false</i></b>
*/
boolean
inheritColumns
()
default
false
;
/**
* <b>createIfRequired</b> allows user to control whether or not
* JaQu tries to create the table and indexes.
* <p>
* <b>Default: <i>true</i></b>
*/
boolean
createIfRequired
()
default
true
;
/**
* <b>strictTypeMapping</b> allows user to specify that only supported
* types are mapped.<br>
* If set <i>true</i>, unsupported mapped types will throw a
* RuntimeException.<br>
* If set <i>false</i>, unsupported mapped types will default to
* VARCHAR.
* <p>
* <b>Default: <i>true</i></b>
*/
boolean
strictTypeMapping
()
default
true
;
/**
* <b>annotationsOnly</b> controls reflective field mapping on your
* model object. If set <i>true</i>, only fields that are explicitly
* annotated as JQColumn are mapped.
* <p>
* <b>Default: <i>true</i></b>
*/
boolean
annotationsOnly
()
default
true
;
/**
* If <b>memoryTable</b> is set <i>true</i>, this table is created as a
* memory table where data is persistent, but index data is kept in main
* memory.<br>
* The JDBC Connection class is verified before applying this property
* in the CREATE phase.
* <p>
* <b>Default: <i>false</i></b>
* <p>
* <u>Valid only for H2 databases.</u>
*/
boolean
memoryTable
()
default
false
;
/**
* If <b>version</b> is set to a <i>non-zero</i> value, JaQu will
* maintain a "_jq_versions" table within your database. The
* <i>version</i> number will be used to call to a registered
* <i>DbUpgrader</i> implementation to perform relevant ALTERs or
* whatever.
* <p>
* <b>Default: <i>0</i></b>
* <p>
* <bNOTE:</b><br>
* You must specify a <i>DbUpgrader</i> on your <i>Db</i> object to
* use this parameter.
*/
int
version
()
default
0
;
}
/**
* Annotation to define a Column. Annotated fields may have any Scope with
* the understanding that under some circumstances, the JVM may raise a
* SecurityException.
*/
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
(
ElementType
.
FIELD
)
public
@interface
JQColumn
{
/**
* If <b>name</b> is not specified the instance variable field name will
* be used as the column name.
* <p>
* <b>Default: <i>reflective field name mapping</i></b>
*/
String
name
()
default
""
;
/**
* If <b>primaryKey</b> is true, this column will be the PrimaryKey.
* <p>
* <b>Default: <i>false</i></b>
*/
boolean
primaryKey
()
default
false
;
/**
* If <b>autoIncrement</b> is true, the column will be created with a
* sequence as the default value.
* <p>
* <b>Default: <i>false</i></b>
*/
boolean
autoIncrement
()
default
false
;
/**
* If <b>maxLength</b> > 0 it is used during the CREATE TABLE phase. It
* may also be optionally used to prevent database exceptions on INSERT
* and UPDATE statements (see <i>trimString</i>).
* <p>
* Any maxLength set in <i>define()</i> may override this annotation
* setting if the model class is not annotated with JQTable.
* <p>
* <b>Default: <i>0</i></b>
*/
int
maxLength
()
default
0
;
/**
* If <b>trimString</b> is <i>true</i> JaQu will automatically trim the
* string if it exceeds <b>maxLength</b>.
* <p>
* e.g. stringValue = stringValue.substring(0, maxLength)
* <p>
* <b>Default: <i>false</i></b>
*/
boolean
trimString
()
default
false
;
/**
* If <b>allowNull</b> is <i>false</i> then JaQu will set
* the column NOT NULL during the CREATE TABLE phase.
* <p>
* <b>Default: <i>false</i></b>
*/
boolean
allowNull
()
default
false
;
/**
* <b>defaultValue</b> is the value assigned to the column during the
* CREATE TABLE phase.
* <p>
* To set <b>null</b>, defaultValue="" (default)
* <p>
* This field could contain a literal <u>single-quoted value</u>.<br>
* Or a function call.<br>
* Empty strings will be considered NULL.
* <ul>
* <li>defaultValue="" (null)
* <li>defaultValue="CURRENT_TIMESTAMP" (H2 current_timestamp())
* <li>defaultValue="''" (default empty string)
* <li>defaultValue="'0'" (default number)
* <li>defaultValue="'1970-01-01 00:00:01'" (default date)
* </ul>
* if (
* <ul>
* <li>defaultValue is properly specified
* <li>AND <i>autoIncrement</i> == false
* <li>AND <i>primaryKey</i> == false
* </ul>
* )<br>
* then this value will be included in the "DEFAULT ..." phrase of a
* column during the CREATE TABLE process.
* <p>
* <b>Default: <i>unspecified, null</i></b>
*/
String
defaultValue
()
default
""
;
}
/**
* This method is called to let the table define the primary key, indexes,
* and the table name.
...
...
h2/src/tools/org/h2/jaqu/TableDefinition.java
浏览文件 @
0bedabe6
...
...
@@ -8,15 +8,24 @@ package org.h2.jaqu;
//## Java 1.5 begin ##
import
java.lang.reflect.Field
;
import
java.lang.reflect.Modifier
;
import
java.sql.ResultSet
;
import
java.sql.SQLException
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.IdentityHashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
org.h2.jaqu.Table.IndexType
;
import
org.h2.jaqu.Table.JQColumn
;
import
org.h2.jaqu.Table.JQIndex
;
import
org.h2.jaqu.Table.JQSchema
;
import
org.h2.jaqu.Table.JQTable
;
import
org.h2.jaqu.util.StatementLogger
;
import
org.h2.jaqu.util.StatementBuilder
;
import
org.h2.jaqu.util.StringUtils
;
import
org.h2.jaqu.util.Utils
;
import
org.h2.util.StatementBuilder
;
/**
* A table definition contains the index definitions of a table, the field
...
...
@@ -33,9 +42,9 @@ class TableDefinition<T> {
*/
//## Java 1.5 begin ##
static
class
IndexDefinition
{
boolean
uniqu
e
;
IndexType
typ
e
;
String
indexName
;
Lis
t
<
String
>
columnNames
;
Se
t
<
String
>
columnNames
;
}
//## Java 1.5 end ##
...
...
@@ -49,6 +58,10 @@ class TableDefinition<T> {
String
dataType
;
int
maxLength
;
boolean
isPrimaryKey
;
boolean
isAutoIncrement
;
boolean
trimString
;
boolean
allowNull
;
String
defaultValue
;
Object
getValue
(
Object
obj
)
{
try
{
...
...
@@ -65,6 +78,8 @@ class TableDefinition<T> {
void
setValue
(
Object
obj
,
Object
o
)
{
try
{
if
(!
field
.
isAccessible
())
field
.
setAccessible
(
true
);
o
=
Utils
.
convert
(
o
,
field
.
getType
());
field
.
set
(
obj
,
o
);
}
catch
(
Exception
e
)
{
...
...
@@ -80,24 +95,37 @@ class TableDefinition<T> {
}
}
}
private
boolean
createTableIfRequired
=
true
;
String
schemaName
;
String
tableName
;
private
Class
<
T
>
clazz
;
private
ArrayList
<
FieldDefinition
>
fields
=
Utils
.
newArrayList
();
private
IdentityHashMap
<
Object
,
FieldDefinition
>
fieldMap
=
Utils
.
newIdentityHashMap
();
private
Lis
t
<
String
>
primaryKeyColumnNames
;
private
Se
t
<
String
>
primaryKeyColumnNames
;
private
ArrayList
<
IndexDefinition
>
indexes
=
Utils
.
newArrayList
();
private
boolean
memoryTable
=
false
;
int
tableVersion
=
0
;
TableDefinition
(
Class
<
T
>
clazz
)
{
this
.
clazz
=
clazz
;
tableName
=
clazz
.
getSimpleName
();
schemaName
=
null
;
tableName
=
clazz
.
getSimpleName
();
}
Class
<
T
>
getModelClass
()
{
return
clazz
;
}
List
<
FieldDefinition
>
getFields
()
{
return
fields
;
}
void
setSchemaName
(
String
schemaName
)
{
this
.
schemaName
=
schemaName
;
}
void
setTableName
(
String
tableName
)
{
this
.
tableName
=
tableName
;
}
...
...
@@ -111,23 +139,41 @@ class TableDefinition<T> {
}
}
void
setPrimaryKey
(
List
<
String
>
primaryKeyColumnNames
)
{
this
.
primaryKeyColumnNames
=
Utils
.
newHashSet
(
primaryKeyColumnNames
);
// set isPrimaryKey flag for all field definitions
for
(
FieldDefinition
fieldDefinition
:
fieldMap
.
values
())
{
fieldDefinition
.
isPrimaryKey
=
this
.
primaryKeyColumnNames
.
contains
(
fieldDefinition
.
columnName
);
}
}
<
A
>
String
getColumnName
(
A
fieldObject
)
{
FieldDefinition
def
=
fieldMap
.
get
(
fieldObject
);
return
def
==
null
?
null
:
def
.
columnName
;
}
private
Lis
t
<
String
>
mapColumnNames
(
Object
[]
columns
)
{
List
<
String
>
columnNames
=
Utils
.
newArrayLis
t
();
private
Se
t
<
String
>
mapColumnNames
(
Object
[]
columns
)
{
Set
<
String
>
columnNames
=
Utils
.
newHashSe
t
();
for
(
Object
column
:
columns
)
{
columnNames
.
add
(
getColumnName
(
column
));
}
return
columnNames
;
}
void
addIndex
(
Object
[]
columns
)
{
void
addIndex
(
IndexType
type
,
Object
[]
columns
)
{
IndexDefinition
index
=
new
IndexDefinition
();
index
.
indexName
=
tableName
+
"_"
+
indexes
.
size
();
index
.
columnNames
=
mapColumnNames
(
columns
);
index
.
type
=
type
;
indexes
.
add
(
index
);
}
void
addIndex
(
IndexType
type
,
List
<
String
>
columnNames
)
{
IndexDefinition
index
=
new
IndexDefinition
();
index
.
indexName
=
tableName
+
"_"
+
indexes
.
size
();
index
.
columnNames
=
Utils
.
newHashSet
(
columnNames
);
index
.
type
=
type
;
indexes
.
add
(
index
);
}
...
...
@@ -142,43 +188,89 @@ class TableDefinition<T> {
}
void
mapFields
()
{
Field
[]
classFields
=
clazz
.
getFields
();
boolean
byAnnotationsOnly
=
false
;
boolean
inheritColumns
=
false
;
boolean
strictTypeMapping
=
false
;
if
(
clazz
.
isAnnotationPresent
(
JQTable
.
class
))
{
JQTable
tableAnnotation
=
clazz
.
getAnnotation
(
JQTable
.
class
);
byAnnotationsOnly
=
tableAnnotation
.
annotationsOnly
();
inheritColumns
=
tableAnnotation
.
inheritColumns
();
strictTypeMapping
=
tableAnnotation
.
strictTypeMapping
();
}
List
<
Field
>
classFields
=
Utils
.
newArrayList
();
classFields
.
addAll
(
Arrays
.
asList
(
clazz
.
getDeclaredFields
()));
if
(
inheritColumns
)
{
Class
<?>
superClazz
=
clazz
.
getSuperclass
();
classFields
.
addAll
(
Arrays
.
asList
(
superClazz
.
getDeclaredFields
()));
}
for
(
Field
f
:
classFields
)
{
FieldDefinition
fieldDef
=
new
FieldDefinition
();
fieldDef
.
field
=
f
;
fieldDef
.
columnName
=
f
.
getName
();
fieldDef
.
dataType
=
getDataType
(
f
);
fields
.
add
(
fieldDef
);
String
columnName
=
f
.
getName
();
// default to field name
boolean
isAutoIncrement
=
false
;
boolean
isPrimaryKey
=
false
;
int
maxLength
=
0
;
boolean
trimString
=
false
;
boolean
allowNull
=
true
;
String
defaultValue
=
""
;
boolean
hasAnnotation
=
f
.
isAnnotationPresent
(
JQColumn
.
class
);
if
(
hasAnnotation
)
{
JQColumn
col
=
f
.
getAnnotation
(
JQColumn
.
class
);
if
(!
StringUtils
.
isNullOrEmpty
(
col
.
name
()))
columnName
=
col
.
name
();
isAutoIncrement
=
col
.
autoIncrement
();
isPrimaryKey
=
col
.
primaryKey
();
maxLength
=
col
.
maxLength
();
trimString
=
col
.
trimString
();
allowNull
=
col
.
allowNull
();
defaultValue
=
col
.
defaultValue
();
}
boolean
isPublic
=
Modifier
.
isPublic
(
f
.
getModifiers
());
boolean
reflectiveMatch
=
isPublic
&&
!
byAnnotationsOnly
;
if
(
reflectiveMatch
||
hasAnnotation
)
{
FieldDefinition
fieldDef
=
new
FieldDefinition
();
fieldDef
.
field
=
f
;
fieldDef
.
columnName
=
columnName
;
fieldDef
.
isAutoIncrement
=
isAutoIncrement
;
fieldDef
.
isPrimaryKey
=
isPrimaryKey
;
fieldDef
.
maxLength
=
maxLength
;
fieldDef
.
trimString
=
trimString
;
fieldDef
.
allowNull
=
allowNull
;
fieldDef
.
defaultValue
=
defaultValue
;
fieldDef
.
dataType
=
ModelUtils
.
getDataType
(
fieldDef
,
strictTypeMapping
);
fields
.
add
(
fieldDef
);
}
}
List
<
String
>
primaryKey
=
Utils
.
newArrayList
();
for
(
FieldDefinition
fieldDef
:
fields
)
{
if
(
fieldDef
.
isPrimaryKey
)
primaryKey
.
add
(
fieldDef
.
columnName
);
}
if
(
primaryKey
.
size
()
>
0
)
setPrimaryKey
(
primaryKey
);
}
private
static
String
getDataType
(
Field
field
)
{
Class
<?>
fieldClass
=
field
.
getType
();
if
(
fieldClass
==
Integer
.
class
)
{
return
"INT"
;
}
else
if
(
fieldClass
==
String
.
class
)
{
return
"VARCHAR"
;
}
else
if
(
fieldClass
==
Double
.
class
)
{
return
"DOUBLE"
;
}
else
if
(
fieldClass
==
java
.
math
.
BigDecimal
.
class
)
{
return
"DECIMAL"
;
}
else
if
(
fieldClass
==
java
.
util
.
Date
.
class
)
{
return
"DATE"
;
}
else
if
(
fieldClass
==
java
.
sql
.
Date
.
class
)
{
return
"DATE"
;
}
else
if
(
fieldClass
==
java
.
sql
.
Time
.
class
)
{
return
"TIME"
;
}
else
if
(
fieldClass
==
java
.
sql
.
Timestamp
.
class
)
{
return
"TIMESTAMP"
;
}
return
"VARCHAR"
;
// TODO add more data types
// Optionally truncates strings to maxLength
private
Object
getValue
(
Object
obj
,
FieldDefinition
field
)
{
Object
value
=
field
.
getValue
(
obj
);
if
(
field
.
trimString
&&
field
.
maxLength
>
0
)
{
if
(
value
instanceof
String
)
{
// Clip Strings
String
s
=
(
String
)
value
;
if
(
s
.
length
()
>
field
.
maxLength
)
return
s
.
substring
(
0
,
field
.
maxLength
);
return
s
;
}
return
value
;
}
else
// Standard JaQu behavior
return
value
;
}
void
insert
(
Db
db
,
Object
obj
)
{
long
insert
(
Db
db
,
Object
obj
,
boolean
returnKey
)
{
SQLStatement
stat
=
new
SQLStatement
(
db
);
StatementBuilder
buff
=
new
StatementBuilder
(
"INSERT INTO "
);
buff
.
append
(
tableName
).
append
(
'('
);
buff
.
append
(
db
.
getDialect
().
tableName
(
schemaName
,
tableName
)
).
append
(
'('
);
for
(
FieldDefinition
field
:
fields
)
{
buff
.
appendExceptFirst
(
", "
);
buff
.
append
(
field
.
columnName
);
...
...
@@ -188,12 +280,15 @@ class TableDefinition<T> {
for
(
FieldDefinition
field
:
fields
)
{
buff
.
appendExceptFirst
(
", "
);
buff
.
append
(
'?'
);
Object
value
=
field
.
getValue
(
obj
);
Object
value
=
getValue
(
obj
,
field
);
stat
.
addParameter
(
value
);
}
buff
.
append
(
')'
);
stat
.
setSQL
(
buff
.
toString
());
stat
.
executeUpdate
();
StatementLogger
.
insert
(
stat
.
getSQL
());
if
(
returnKey
)
return
stat
.
executeInsert
();
return
stat
.
executeUpdate
();
}
void
merge
(
Db
db
,
Object
obj
)
{
...
...
@@ -203,7 +298,7 @@ class TableDefinition<T> {
}
SQLStatement
stat
=
new
SQLStatement
(
db
);
StatementBuilder
buff
=
new
StatementBuilder
(
"MERGE INTO "
);
buff
.
append
(
tableName
).
append
(
" ("
);
buff
.
append
(
db
.
getDialect
().
tableName
(
schemaName
,
tableName
)
).
append
(
" ("
);
buff
.
resetCount
();
for
(
FieldDefinition
field
:
fields
)
{
buff
.
appendExceptFirst
(
", "
);
...
...
@@ -223,11 +318,12 @@ class TableDefinition<T> {
for
(
FieldDefinition
field
:
fields
)
{
buff
.
appendExceptFirst
(
", "
);
buff
.
append
(
'?'
);
Object
value
=
field
.
getValue
(
obj
);
Object
value
=
getValue
(
obj
,
field
);
stat
.
addParameter
(
value
);
}
buff
.
append
(
')'
);
stat
.
setSQL
(
buff
.
toString
());
StatementLogger
.
merge
(
stat
.
getSQL
());
stat
.
executeUpdate
();
}
...
...
@@ -238,20 +334,52 @@ class TableDefinition<T> {
}
SQLStatement
stat
=
new
SQLStatement
(
db
);
StatementBuilder
buff
=
new
StatementBuilder
(
"UPDATE "
);
buff
.
append
(
tableName
).
append
(
" SET "
);
buff
.
append
(
db
.
getDialect
().
tableName
(
schemaName
,
tableName
)
).
append
(
" SET "
);
buff
.
resetCount
();
for
(
FieldDefinition
field
:
fields
)
{
if
(!
field
.
isPrimaryKey
)
{
buff
.
appendExceptFirst
(
", "
);
buff
.
append
(
field
.
columnName
);
buff
.
append
(
" = ?"
);
Object
value
=
field
.
getValue
(
obj
);
Object
value
=
getValue
(
obj
,
field
);
stat
.
addParameter
(
value
);
}
}
Object
alias
=
Utils
.
newObject
(
obj
.
getClass
());
Query
<
Object
>
query
=
Query
.
from
(
db
,
alias
);
boolean
firstCondition
=
true
;
for
(
FieldDefinition
field
:
fields
)
{
if
(
field
.
isPrimaryKey
)
{
Object
aliasValue
=
field
.
getValue
(
alias
);
Object
value
=
field
.
getValue
(
obj
);
if
(!
firstCondition
)
{
query
.
addConditionToken
(
ConditionAndOr
.
AND
);
}
firstCondition
=
false
;
query
.
addConditionToken
(
new
Condition
<
Object
>(
aliasValue
,
value
,
CompareType
.
EQUAL
));
}
}
stat
.
setSQL
(
buff
.
toString
());
query
.
appendWhere
(
stat
);
StatementLogger
.
update
(
stat
.
getSQL
());
stat
.
executeUpdate
();
}
void
delete
(
Db
db
,
Object
obj
)
{
if
(
primaryKeyColumnNames
==
null
||
primaryKeyColumnNames
.
size
()
==
0
)
{
throw
new
IllegalStateException
(
"No primary key columns defined "
+
"for table "
+
obj
.
getClass
()
+
" - no update possible"
);
}
SQLStatement
stat
=
new
SQLStatement
(
db
);
StatementBuilder
buff
=
new
StatementBuilder
(
"DELETE FROM "
);
buff
.
append
(
db
.
getDialect
().
tableName
(
schemaName
,
tableName
));
buff
.
resetCount
();
Object
alias
=
Utils
.
newObject
(
obj
.
getClass
());
Query
<
Object
>
query
=
Query
.
from
(
db
,
alias
);
boolean
firstCondition
=
true
;
for
(
FieldDefinition
field
:
fields
)
{
if
(
field
.
isPrimaryKey
)
{
Object
aliasValue
=
field
.
getValue
(
alias
);
...
...
@@ -267,21 +395,60 @@ class TableDefinition<T> {
}
stat
.
setSQL
(
buff
.
toString
());
query
.
appendWhere
(
stat
);
StatementLogger
.
delete
(
stat
.
getSQL
());
stat
.
executeUpdate
();
}
TableDefinition
<
T
>
createTableIfRequired
(
Db
db
)
{
if
(!
createTableIfRequired
)
{
// Skip table and index creation
// But still check for upgrades
db
.
upgradeTable
(
this
);
return
this
;
}
SQLStatement
stat
=
new
SQLStatement
(
db
);
StatementBuilder
buff
=
new
StatementBuilder
(
"CREATE TABLE IF NOT EXISTS "
);
buff
.
append
(
tableName
).
append
(
'('
);
StatementBuilder
buff
;
if
(
memoryTable
&&
db
.
getConnection
().
getClass
()
.
getCanonicalName
().
equals
(
"org.h2.jdbc.JdbcConnection"
))
buff
=
new
StatementBuilder
(
"CREATE MEMORY TABLE IF NOT EXISTS "
);
else
buff
=
new
StatementBuilder
(
"CREATE TABLE IF NOT EXISTS "
);
buff
.
append
(
db
.
getDialect
().
tableName
(
schemaName
,
tableName
)).
append
(
'('
);
for
(
FieldDefinition
field
:
fields
)
{
buff
.
appendExceptFirst
(
", "
);
buff
.
appendExceptFirst
(
", "
);
buff
.
append
(
field
.
columnName
).
append
(
' '
).
append
(
field
.
dataType
);
if
(
field
.
maxLength
!=
0
)
{
// FIELD LENGTH
if
(
field
.
maxLength
>
0
)
{
buff
.
append
(
'('
).
append
(
field
.
maxLength
).
append
(
')'
);
}
// AUTO_INCREMENT
if
(
field
.
isAutoIncrement
)
{
buff
.
append
(
" AUTO_INCREMENT"
);
}
// NOT NULL
if
(!
field
.
allowNull
)
{
buff
.
append
(
" NOT NULL"
);
}
// DEFAULT...
if
(!
field
.
isAutoIncrement
&&
!
field
.
isPrimaryKey
)
{
String
dv
=
field
.
defaultValue
;
if
(!
StringUtils
.
isNullOrEmpty
(
dv
))
{
if
(
ModelUtils
.
isProperlyFormattedDefaultValue
(
dv
)
&&
ModelUtils
.
isValidDefaultValue
(
field
.
field
.
getType
(),
dv
))
{
buff
.
append
(
" DEFAULT "
+
dv
);
}
}
}
}
if
(
primaryKeyColumnNames
!=
null
)
{
// PRIMARY KEY...
if
(
primaryKeyColumnNames
!=
null
&&
primaryKeyColumnNames
.
size
()
>
0
)
{
buff
.
append
(
", PRIMARY KEY("
);
buff
.
resetCount
();
for
(
String
n
:
primaryKeyColumnNames
)
{
...
...
@@ -292,14 +459,94 @@ class TableDefinition<T> {
}
buff
.
append
(
')'
);
stat
.
setSQL
(
buff
.
toString
());
StatementLogger
.
create
(
stat
.
getSQL
());
stat
.
executeUpdate
();
// TODO create indexes
// Create Indexes
for
(
IndexDefinition
index:
indexes
)
{
String
sql
=
db
.
getDialect
().
createIndex
(
schemaName
,
tableName
,
index
);
stat
.
setSQL
(
sql
);
StatementLogger
.
create
(
stat
.
getSQL
());
stat
.
executeUpdate
();
}
// Table is created IF NOT EXISTS, otherwise statement is ignored
// But we still need to process potential Upgrade
db
.
upgradeTable
(
this
);
return
this
;
}
// Retrieve list of columns from CSV whitespace notated index
private
List
<
String
>
getColumns
(
String
index
)
{
List
<
String
>
cols
=
Utils
.
newArrayList
();
if
(
index
==
null
||
index
.
length
()
==
0
)
return
null
;
String
[]
cs
=
index
.
split
(
"(,|\\s)"
);
for
(
String
c
:
cs
)
if
(
c
!=
null
&&
c
.
trim
().
length
()
>
0
)
cols
.
add
(
c
.
trim
());
if
(
cols
.
size
()
==
0
)
return
null
;
return
cols
;
}
void
mapObject
(
Object
obj
)
{
fieldMap
.
clear
();
initObject
(
obj
,
fieldMap
);
if
(
clazz
.
isAnnotationPresent
(
JQSchema
.
class
))
{
JQSchema
schemaAnnotation
=
clazz
.
getAnnotation
(
JQSchema
.
class
);
// Setup Schema name mapping, if properly annotated
if
(!
StringUtils
.
isNullOrEmpty
(
schemaAnnotation
.
name
()))
schemaName
=
schemaAnnotation
.
name
();
}
if
(
clazz
.
isAnnotationPresent
(
JQTable
.
class
))
{
JQTable
tableAnnotation
=
clazz
.
getAnnotation
(
JQTable
.
class
);
// Setup Table name mapping, if properly annotated
if
(!
StringUtils
.
isNullOrEmpty
(
tableAnnotation
.
name
()))
tableName
=
tableAnnotation
.
name
();
// Allow control over createTableIfRequired()
createTableIfRequired
=
tableAnnotation
.
createIfRequired
();
// Model Version
if
(
tableAnnotation
.
version
()
>
0
)
tableVersion
=
tableAnnotation
.
version
();
// Setup the Primary Index, if properly annotated
List
<
String
>
primaryKey
=
getColumns
(
tableAnnotation
.
primaryKey
());
if
(
primaryKey
!=
null
)
setPrimaryKey
(
primaryKey
);
}
if
(
clazz
.
isAnnotationPresent
(
JQIndex
.
class
))
{
JQIndex
indexAnnotation
=
clazz
.
getAnnotation
(
JQIndex
.
class
);
// Setup the indexes, if properly annotated
addIndexes
(
IndexType
.
STANDARD
,
indexAnnotation
.
standard
());
addIndexes
(
IndexType
.
UNIQUE
,
indexAnnotation
.
unique
());
addIndexes
(
IndexType
.
HASH
,
indexAnnotation
.
hash
());
addIndexes
(
IndexType
.
UNIQUE_HASH
,
indexAnnotation
.
uniqueHash
());
}
}
void
addIndexes
(
IndexType
type
,
String
[]
indexes
)
{
for
(
String
index:
indexes
)
{
List
<
String
>
validatedColumns
=
getColumns
(
index
);
if
(
validatedColumns
==
null
)
return
;
addIndex
(
type
,
validatedColumns
);
}
}
List
<
IndexDefinition
>
getIndexes
(
IndexType
type
)
{
List
<
IndexDefinition
>
list
=
Utils
.
newArrayList
();
for
(
IndexDefinition
def:
indexes
)
if
(
def
.
type
.
equals
(
type
))
list
.
add
(
def
);
return
list
;
}
void
initObject
(
Object
obj
,
Map
<
Object
,
FieldDefinition
>
map
)
{
...
...
h2/src/tools/org/h2/jaqu/TableInspector.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: James Moger
*/
package
org
.
h2
.
jaqu
;
import
static
java
.
text
.
MessageFormat
.
format
;
import
static
org
.
h2
.
jaqu
.
Validation
.
CONSIDER
;
import
static
org
.
h2
.
jaqu
.
Validation
.
ERROR
;
import
static
org
.
h2
.
jaqu
.
Validation
.
WARN
;
import
static
org
.
h2
.
jaqu
.
util
.
JdbcUtils
.
closeSilently
;
import
static
org
.
h2
.
jaqu
.
util
.
StringUtils
.
isNullOrEmpty
;
import
java.lang.reflect.Modifier
;
import
java.sql.DatabaseMetaData
;
import
java.sql.ResultSet
;
import
java.sql.SQLException
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.Date
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
org.h2.jaqu.Table.IndexType
;
import
org.h2.jaqu.Table.JQColumn
;
import
org.h2.jaqu.Table.JQIndex
;
import
org.h2.jaqu.Table.JQSchema
;
import
org.h2.jaqu.Table.JQTable
;
import
org.h2.jaqu.TableDefinition.FieldDefinition
;
import
org.h2.jaqu.TableDefinition.IndexDefinition
;
import
org.h2.jaqu.util.StatementBuilder
;
import
org.h2.jaqu.util.Utils
;
/**
* Class to inspect the contents of a particular table including its indexes.
* This class does the bulk of the work in terms of model generation and model
* validation.
*/
public
class
TableInspector
{
String
schema
;
String
table
;
boolean
forceUpperCase
;
Class
<?
extends
java
.
util
.
Date
>
dateClazz
;
List
<
String
>
primaryKeys
=
Utils
.
newArrayList
();
Map
<
String
,
IndexInspector
>
indexes
;
Map
<
String
,
ColumnInspector
>
columns
;
final
String
eol
=
"\n"
;
TableInspector
(
String
schema
,
String
table
,
boolean
forceUpperCase
,
Class
<?
extends
java
.
util
.
Date
>
dateClazz
)
{
this
.
schema
=
schema
;
this
.
table
=
table
;
this
.
forceUpperCase
=
forceUpperCase
;
this
.
dateClazz
=
dateClazz
;
}
/**
* Tests to see if this TableInspector represents schema.table.
* <p>
* @param schema
* @param table
* @return
*/
boolean
matches
(
String
schema
,
String
table
)
{
if
(
isNullOrEmpty
(
schema
))
// table name matching
return
this
.
table
.
equalsIgnoreCase
(
table
);
else
if
(
isNullOrEmpty
(
table
))
// schema name matching
return
this
.
schema
.
equalsIgnoreCase
(
schema
);
else
// exact table matching
return
this
.
schema
.
equalsIgnoreCase
(
schema
)
&&
this
.
table
.
equalsIgnoreCase
(
table
);
}
/**
* Reads the DatabaseMetaData for the details of this table including
* primary keys and indexes.
* <p>
* @param metadata
* @throws SQLException
*/
void
read
(
DatabaseMetaData
metadata
)
throws
SQLException
{
ResultSet
rs
=
null
;
// Primary Keys
try
{
rs
=
metadata
.
getPrimaryKeys
(
null
,
schema
,
table
);
while
(
rs
.
next
())
{
String
c
=
rs
.
getString
(
"COLUMN_NAME"
);
primaryKeys
.
add
(
c
);
}
closeSilently
(
rs
);
// Indexes
rs
=
metadata
.
getIndexInfo
(
null
,
schema
,
table
,
false
,
true
);
indexes
=
Utils
.
newHashMap
();
while
(
rs
.
next
())
{
IndexInspector
info
=
new
IndexInspector
(
rs
);
if
(
info
.
type
.
equals
(
IndexType
.
UNIQUE
)
&&
info
.
name
.
toLowerCase
().
startsWith
(
"primary"
))
// Skip PrimaryKey indexes
continue
;
if
(
indexes
.
containsKey
(
info
.
name
))
indexes
.
get
(
info
.
name
).
addColumn
(
rs
);
else
indexes
.
put
(
info
.
name
,
info
);
}
closeSilently
(
rs
);
// Columns
rs
=
metadata
.
getColumns
(
null
,
schema
,
table
,
null
);
columns
=
Utils
.
newHashMap
();
while
(
rs
.
next
())
{
ColumnInspector
col
=
new
ColumnInspector
();
col
.
name
=
rs
.
getString
(
"COLUMN_NAME"
);
col
.
type
=
rs
.
getString
(
"TYPE_NAME"
);
col
.
clazz
=
ModelUtils
.
getClassType
(
col
.
type
,
dateClazz
);
col
.
size
=
rs
.
getInt
(
"COLUMN_SIZE"
);
// Allow Null
try
{
col
.
allowNull
=
(
rs
.
getInt
(
"NULLABLE"
)
==
DatabaseMetaData
.
columnNullable
);
}
catch
(
SQLException
x
)
{
}
// AutoIncrement
try
{
col
.
isAutoIncrement
=
rs
.
getBoolean
(
"IS_AUTOINCREMENT"
);
}
catch
(
SQLException
x
)
{
}
// Primary Key
if
(
primaryKeys
.
size
()
==
1
)
{
if
(
col
.
name
.
equalsIgnoreCase
(
primaryKeys
.
get
(
0
)))
col
.
isPrimaryKey
=
true
;
}
// Default Value
if
(!
col
.
isAutoIncrement
)
{
try
{
col
.
defaultValue
=
rs
.
getString
(
"COLUMN_DEFAULT"
);
}
catch
(
SQLException
t
)
{
try
{
col
.
defaultValue
=
rs
.
getString
(
"COLUMN_DEF"
);
}
catch
(
SQLException
x
)
{
}
}
}
columns
.
put
(
col
.
name
,
col
);
}
}
finally
{
closeSilently
(
rs
);
}
}
/**
* Generates a model (class definition) from this table.
* The model includes indexes, primary keys, default values, maxLengths,
* and allowNull information.
* <p>
* The caller may optionally set a destination package name, whether or not
* ot include the schema name (setting schema can be a problem when using
* the model between databases), and if to automatically trim strings for
* those that have a maximum length.
* <p>
* @param packageName
* @param annotateSchema
* @param trimStrings
* @return
*/
String
generateModel
(
String
packageName
,
boolean
annotateSchema
,
boolean
trimStrings
)
{
// Set of imports
Set
<
String
>
imports
=
Utils
.
newHashSet
();
imports
.
add
(
JQSchema
.
class
.
getCanonicalName
());
imports
.
add
(
JQTable
.
class
.
getCanonicalName
());
imports
.
add
(
JQIndex
.
class
.
getCanonicalName
());
imports
.
add
(
JQColumn
.
class
.
getCanonicalName
());
// Table Fields
StringBuilder
fields
=
new
StringBuilder
();
List
<
ColumnInspector
>
sortedColumns
=
Utils
.
newArrayList
(
columns
.
values
());
Collections
.
sort
(
sortedColumns
);
for
(
ColumnInspector
col
:
sortedColumns
)
fields
.
append
(
generateColumn
(
imports
,
col
,
trimStrings
));
// Build Complete Class Definition
StringBuilder
model
=
new
StringBuilder
();
if
(!
isNullOrEmpty
(
packageName
))
{
// Package
model
.
append
(
"package "
+
packageName
+
";"
);
model
.
append
(
eol
).
append
(
eol
);
}
// Imports
List
<
String
>
sortedImports
=
new
ArrayList
<
String
>(
imports
);
Collections
.
sort
(
sortedImports
);
for
(
String
imp
:
sortedImports
)
model
.
append
(
"import "
).
append
(
imp
).
append
(
';'
).
append
(
eol
);
model
.
append
(
eol
);
// @JQSchema
if
(
annotateSchema
&&
!
isNullOrEmpty
(
schema
))
{
model
.
append
(
'@'
).
append
(
JQSchema
.
class
.
getSimpleName
());
model
.
append
(
'('
);
AnnotationBuilder
ap
=
new
AnnotationBuilder
();
ap
.
addParameter
(
"name"
,
schema
);
model
.
append
(
ap
);
model
.
append
(
')'
).
append
(
eol
);
}
// @JQTable
model
.
append
(
'@'
).
append
(
JQTable
.
class
.
getSimpleName
());
model
.
append
(
'('
);
// JQTable Annotation Parameters
AnnotationBuilder
ap
=
new
AnnotationBuilder
();
ap
.
addParameter
(
"name"
,
table
);
if
(
primaryKeys
.
size
()
>
1
)
{
StringBuilder
pk
=
new
StringBuilder
();
for
(
String
key
:
primaryKeys
)
pk
.
append
(
key
).
append
(
' '
);
pk
.
trimToSize
();
ap
.
addParameter
(
"primaryKey"
,
pk
.
toString
());
}
// Finish @JQTable annotation
model
.
append
(
ap
);
model
.
append
(
')'
).
append
(
eol
);
// @JQIndex
ap
=
new
AnnotationBuilder
();
generateIndexAnnotations
(
ap
,
"standard"
,
IndexType
.
STANDARD
);
generateIndexAnnotations
(
ap
,
"unique"
,
IndexType
.
UNIQUE
);
generateIndexAnnotations
(
ap
,
"hash"
,
IndexType
.
HASH
);
generateIndexAnnotations
(
ap
,
"uniqueHash"
,
IndexType
.
UNIQUE_HASH
);
if
(
ap
.
length
()
>
0
)
{
model
.
append
(
'@'
).
append
(
JQIndex
.
class
.
getSimpleName
());
model
.
append
(
'('
);
model
.
append
(
ap
);
model
.
append
(
')'
).
append
(
eol
);
}
// Class Declaration
String
clazzName
=
ModelUtils
.
createClassName
(
table
);
model
.
append
(
format
(
"public class {0} '{'"
,
clazzName
)).
append
(
eol
);
model
.
append
(
eol
);
// Field Declarations
model
.
append
(
fields
);
// Default Constructor
model
.
append
(
"\tpublic "
).
append
(
clazzName
).
append
(
"() {"
).
append
(
eol
);
model
.
append
(
"\t}"
).
append
(
eol
);
// End of Class Body
model
.
append
(
'}'
);
model
.
trimToSize
();
return
model
.
toString
();
}
/**
* Generates the specified index annotation.
* @param ap
*/
void
generateIndexAnnotations
(
AnnotationBuilder
ap
,
String
parameter
,
IndexType
type
)
{
List
<
IndexInspector
>
list
=
getIndexes
(
type
);
if
(
list
.
size
()
==
0
)
// No matching indexes
return
;
if
(
list
.
size
()
==
1
)
{
ap
.
addParameter
(
parameter
,
list
.
get
(
0
).
getColumnsString
());
}
else
{
List
<
String
>
parameters
=
Utils
.
newArrayList
();
for
(
IndexInspector
index:
list
)
parameters
.
add
(
index
.
getColumnsString
());
ap
.
addParameter
(
parameter
,
parameters
);
}
}
/**
* Returns indexes of a specific type from the map.
* <p>
* @param type
* @return
*/
List
<
IndexInspector
>
getIndexes
(
IndexType
type
)
{
List
<
IndexInspector
>
list
=
Utils
.
newArrayList
();
for
(
IndexInspector
index:
indexes
.
values
())
if
(
index
.
type
.
equals
(
type
))
list
.
add
(
index
);
return
list
;
}
/**
* Generates a column field definition with annotations.
* <p>
* @param imports
* @param col
* @param trimStrings
* @return
*/
StatementBuilder
generateColumn
(
Set
<
String
>
imports
,
ColumnInspector
col
,
boolean
trimStrings
)
{
StatementBuilder
sb
=
new
StatementBuilder
();
Class
<?>
clazz
=
col
.
clazz
;
String
cname
=
ModelUtils
.
createFieldName
(
col
.
name
.
toLowerCase
());
sb
.
append
(
'\t'
);
if
(
clazz
==
null
)
{
// Unsupported Type
clazz
=
Object
.
class
;
sb
.
append
(
"// Unsupported type "
+
col
.
type
);
}
else
{
// @JQColumn
imports
.
add
(
clazz
.
getCanonicalName
());
sb
.
append
(
'@'
).
append
(
JQColumn
.
class
.
getSimpleName
());
// JQColumn Annotation Parameters
AnnotationBuilder
ap
=
new
AnnotationBuilder
();
// JQColumn.name
if
(!
col
.
name
.
equalsIgnoreCase
(
cname
))
{
ap
.
addParameter
(
"name"
,
col
.
name
);
}
// JQColumn.primaryKey
// Composite Primary Keys are annotated on the Table
if
(
col
.
isPrimaryKey
&&
primaryKeys
.
size
()
==
1
)
{
ap
.
addParameter
(
"primaryKey=true"
);
}
// JQColumn.maxLength
if
((
clazz
==
String
.
class
)
&&
(
col
.
size
>
0
)
&&
(
col
.
size
<
Integer
.
MAX_VALUE
))
{
ap
.
addParameter
(
"maxLength"
,
col
.
size
);
// JQColumn.trimStrings
if
(
trimStrings
)
{
ap
.
addParameter
(
"trimString=true"
);
}
}
else
{
// JQColumn.AutoIncrement
if
(
col
.
isAutoIncrement
)
{
ap
.
addParameter
(
"autoIncrement=true"
);
}
}
// JQColumn.allowNull
if
(!
col
.
allowNull
)
{
ap
.
addParameter
(
"allowNull=false"
);
}
// JQColumn.defaultValue
if
(!
isNullOrEmpty
(
col
.
defaultValue
))
ap
.
addParameter
(
"defaultValue=\""
+
col
.
defaultValue
+
"\""
);
// Add leading and trailing ()
if
(
ap
.
length
()
>
0
)
{
ap
.
insert
(
0
,
'('
);
ap
.
append
(
')'
);
}
sb
.
append
(
ap
);
}
sb
.
append
(
eol
);
// Variable Declaration
sb
.
append
(
"\tpublic "
);
sb
.
append
(
clazz
.
getSimpleName
());
sb
.
append
(
' '
);
sb
.
append
(
cname
);
sb
.
append
(
';'
);
sb
.
append
(
eol
).
append
(
eol
);
return
sb
;
}
/**
* Validates that a table definition (annotated, interface, or both) matches
* the current state of the table and indexes in the database.
* <p>
* Results are returned as a List<Validation> which includes recommendations,
* warnings, and errors about the model.
* <p>
* The caller may choose to have validate throw an exception on any validation
* ERROR.
* <p>
* @param <T>
* @param def
* @param throwError
* @return List<Validation>
*/
<
T
>
List
<
Validation
>
validate
(
TableDefinition
<
T
>
def
,
boolean
throwError
)
{
List
<
Validation
>
remarks
=
Utils
.
newArrayList
();
// Model Class Definition Validation
if
(!
Modifier
.
isPublic
(
def
.
getModelClass
().
getModifiers
()))
{
remarks
.
add
(
ERROR
(
table
,
"SCHEMA"
,
format
(
"Class {0} MUST BE PUBLIC!"
,
def
.
getModelClass
().
getCanonicalName
())).
throwError
(
throwError
));
}
// Schema Validation
if
(!
isNullOrEmpty
(
schema
))
{
if
(
isNullOrEmpty
(
def
.
schemaName
))
{
remarks
.
add
(
CONSIDER
(
table
,
"SCHEMA"
,
format
(
"@{0}(name={1})"
,
JQSchema
.
class
.
getSimpleName
(),
schema
)));
}
else
if
(!
schema
.
equalsIgnoreCase
(
def
.
schemaName
))
{
remarks
.
add
(
ERROR
(
table
,
"SCHEMA"
,
format
(
"@{0}(name={1}) != {2}"
,
JQSchema
.
class
.
getSimpleName
(),
def
.
schemaName
,
schema
)).
throwError
(
throwError
));
}
}
// Index Validation
for
(
IndexInspector
index:
indexes
.
values
())
validate
(
remarks
,
def
,
index
,
throwError
);
// Field Column Validation
List
<
FieldDefinition
>
fieldDefs
=
def
.
getFields
();
for
(
FieldDefinition
fieldDef
:
fieldDefs
)
validate
(
remarks
,
fieldDef
,
throwError
);
return
remarks
;
}
/**
* Validates an inspected index from the database against the IndexDefinition
* within the TableDefinition.
* <p>
* <b>TODO</b>: Complete index validation
* <p>
* @param <T>
* @param remarks
* @param def
* @param index
* @param throwError
*/
<
T
>
void
validate
(
List
<
Validation
>
remarks
,
TableDefinition
<
T
>
def
,
IndexInspector
index
,
boolean
throwError
)
{
List
<
IndexDefinition
>
defIndexes
=
def
.
getIndexes
(
IndexType
.
STANDARD
);
List
<
IndexInspector
>
dbIndexes
=
getIndexes
(
IndexType
.
STANDARD
);
if
(
defIndexes
.
size
()
>
dbIndexes
.
size
())
{
remarks
.
add
(
WARN
(
table
,
IndexType
.
STANDARD
.
name
(),
"# of Model Indexes > DB Indexes!"
));
}
else
if
(
defIndexes
.
size
()
<
dbIndexes
.
size
())
{
remarks
.
add
(
WARN
(
table
,
IndexType
.
STANDARD
.
name
(),
"Model class is missing indexes!"
));
}
// TODO Complete index validation.
// Need to actually compare index types and columns within each index.
// At this point my head was starting to hurt.
}
/**
* Validates a column against the model's field definition. Checks for
* existence, supported type, type mapping, default value, defined lengths,
* primary key, autoincrement.
* <p>
* @param remarks
* @param fieldDef
* @param throwError
*/
void
validate
(
List
<
Validation
>
remarks
,
FieldDefinition
fieldDef
,
boolean
throwError
)
{
// Unknown Field
String
fname
=
forceUpperCase
?
fieldDef
.
columnName
.
toUpperCase
()
:
fieldDef
.
columnName
;
if
(!
columns
.
containsKey
(
fname
))
{
// Unknown column mapping!
remarks
.
add
(
ERROR
(
table
,
fieldDef
,
"Does not exist in database!"
).
throwError
(
throwError
));
return
;
}
ColumnInspector
col
=
columns
.
get
(
fname
);
Class
<?>
fieldClazz
=
fieldDef
.
field
.
getType
();
Class
<?>
jdbcClazz
=
ModelUtils
.
getClassType
(
col
.
type
,
dateClazz
);
// Supported Type Check
// JaQu maps to VARCHAR for unsupported types.
if
(
fieldDef
.
dataType
.
equals
(
"VARCHAR"
)
&&
(
fieldClazz
!=
String
.
class
))
remarks
.
add
(
ERROR
(
table
,
fieldDef
,
"JaQu does not currently implement support for "
+
fieldClazz
.
getName
()).
throwError
(
throwError
));
// Number Types
if
(!
fieldClazz
.
equals
(
jdbcClazz
))
{
if
(
Number
.
class
.
isAssignableFrom
(
fieldClazz
))
{
remarks
.
add
(
WARN
(
table
,
col
,
format
(
"Precision Mismatch: ModelObject={0}, ColumnObject={1}"
,
fieldClazz
.
getSimpleName
(),
jdbcClazz
.
getSimpleName
())));
}
else
{
if
(!
Date
.
class
.
isAssignableFrom
(
jdbcClazz
))
{
remarks
.
add
(
WARN
(
table
,
col
,
format
(
"Object Mismatch: ModelObject={0}, ColumnObject={1}"
,
fieldClazz
.
getSimpleName
(),
jdbcClazz
.
getSimpleName
())));
}
}
}
// String Types
if
(
fieldClazz
==
String
.
class
)
{
if
((
fieldDef
.
maxLength
!=
col
.
size
)
&&
(
col
.
size
<
Integer
.
MAX_VALUE
))
remarks
.
add
(
WARN
(
table
,
col
,
format
(
"{0}.maxLength={1}, ColumnMaxLength={2}"
,
JQColumn
.
class
.
getSimpleName
(),
fieldDef
.
maxLength
,
col
.
size
)));
if
(
fieldDef
.
maxLength
>
0
&&
!
fieldDef
.
trimString
)
remarks
.
add
(
CONSIDER
(
table
,
col
,
format
(
"{0}.truncateToMaxLength=true"
+
" will prevent RuntimeExceptions on"
+
" INSERTs or UPDATEs, but will clip data!"
,
JQColumn
.
class
.
getSimpleName
())));
}
// Numeric AutoIncrement
if
(
fieldDef
.
isAutoIncrement
!=
col
.
isAutoIncrement
)
remarks
.
add
(
WARN
(
table
,
col
,
format
(
"{0}.isAutoIncrement={1}"
+
" while Column autoIncrement={2}"
,
JQColumn
.
class
.
getSimpleName
(),
fieldDef
.
isAutoIncrement
,
col
.
isAutoIncrement
)));
// Last Check
// Default Value...
if
(!
col
.
isAutoIncrement
&&
!
col
.
isPrimaryKey
)
{
// Check Model.defaultValue Format
if
(!
ModelUtils
.
isProperlyFormattedDefaultValue
(
fieldDef
.
defaultValue
))
{
remarks
.
add
(
ERROR
(
table
,
col
,
format
(
"{0}.defaultValue=\"{1}\""
+
" is improperly formatted!"
,
JQColumn
.
class
.
getSimpleName
(),
fieldDef
.
defaultValue
)).
throwError
(
throwError
));
// Next field
return
;
}
// Compare Model.defaultValue to Column.defaultValue
if
(
isNullOrEmpty
(
fieldDef
.
defaultValue
)
&&
!
isNullOrEmpty
(
col
.
defaultValue
))
{
// Model.defaultValue is NULL, Column.defaultValue is NOT NULL
remarks
.
add
(
WARN
(
table
,
col
,
format
(
"{0}.defaultValue=\"\""
+
" while Column default=\"{1}\""
,
JQColumn
.
class
.
getSimpleName
(),
col
.
defaultValue
)));
}
else
if
(!
isNullOrEmpty
(
fieldDef
.
defaultValue
)
&&
isNullOrEmpty
(
col
.
defaultValue
))
{
// Column.defaultValue is NULL, Model.defaultValue is NOT NULL
remarks
.
add
(
WARN
(
table
,
col
,
format
(
"{0}.defaultValue=\"{1}\""
+
" while Column default=\"\""
,
JQColumn
.
class
.
getSimpleName
(),
fieldDef
.
defaultValue
)));
}
else
if
(!
isNullOrEmpty
(
fieldDef
.
defaultValue
)
&&
!
isNullOrEmpty
(
col
.
defaultValue
))
{
if
(!
fieldDef
.
defaultValue
.
equals
(
col
.
defaultValue
))
{
// Model.defaultValue != Column.defaultValue
remarks
.
add
(
WARN
(
table
,
col
,
format
(
"{0}.defaultValue=\"{1}\""
+
" while Column default=\"{2}\""
,
JQColumn
.
class
.
getSimpleName
(),
fieldDef
.
defaultValue
,
col
.
defaultValue
)));
}
}
// Sanity Check Model.defaultValue Literal Value
if
(!
ModelUtils
.
isValidDefaultValue
(
fieldDef
.
field
.
getType
(),
fieldDef
.
defaultValue
))
{
remarks
.
add
(
ERROR
(
table
,
col
,
format
(
"{0}.defaultValue=\"{1}\" is invalid!!"
,
JQColumn
.
class
.
getSimpleName
(),
fieldDef
.
defaultValue
)));
}
}
}
/**
* Represents an index as it exists in the database.
*
*/
public
static
class
IndexInspector
{
String
name
;
IndexType
type
;
private
List
<
String
>
columns
=
new
ArrayList
<
String
>();
public
IndexInspector
(
ResultSet
rs
)
throws
SQLException
{
name
=
rs
.
getString
(
"INDEX_NAME"
);
// Determine Index Type
boolean
hash
=
rs
.
getInt
(
"TYPE"
)
==
DatabaseMetaData
.
tableIndexHashed
;
boolean
unique
=
!
rs
.
getBoolean
(
"NON_UNIQUE"
);
if
(!
hash
&&
!
unique
)
type
=
IndexType
.
STANDARD
;
else
if
(
hash
&&
unique
)
type
=
IndexType
.
UNIQUE_HASH
;
else
if
(
unique
)
type
=
IndexType
.
UNIQUE
;
else
if
(
hash
)
type
=
IndexType
.
HASH
;
columns
.
add
(
rs
.
getString
(
"COLUMN_NAME"
));
}
public
void
addColumn
(
ResultSet
rs
)
throws
SQLException
{
columns
.
add
(
rs
.
getString
(
"COLUMN_NAME"
));
}
public
String
getColumnsString
()
{
StatementBuilder
sb
=
new
StatementBuilder
();
for
(
String
col
:
columns
)
{
sb
.
appendExceptFirst
(
", "
);
sb
.
append
(
col
);
}
return
sb
.
toString
().
trim
();
}
}
/**
* Represents a column as it exists in the database.
*
*/
public
static
class
ColumnInspector
implements
Comparable
<
ColumnInspector
>
{
String
name
=
null
;
String
type
=
null
;
int
size
=
0
;
boolean
allowNull
=
false
;
Class
<?>
clazz
=
null
;
boolean
isPrimaryKey
=
false
;
boolean
isAutoIncrement
=
false
;
String
defaultValue
=
null
;
@Override
public
int
compareTo
(
ColumnInspector
o
)
{
if
(
isPrimaryKey
&&
o
.
isPrimaryKey
)
// both primary sort by name
return
name
.
compareTo
(
o
.
name
);
else
if
(
isPrimaryKey
&&
!
o
.
isPrimaryKey
)
// primary first
return
-
1
;
else
if
(!
isPrimaryKey
&&
o
.
isPrimaryKey
)
// primary first
return
1
;
else
// Neither primary, sort by name
return
name
.
compareTo
(
o
.
name
);
}
}
/**
* Convenience class based on StatementBuilder for creating the
* annotation parameter list.
*
*/
private
static
class
AnnotationBuilder
extends
StatementBuilder
{
AnnotationBuilder
()
{
super
();
}
void
addParameter
(
String
parameter
)
{
appendExceptFirst
(
", "
);
append
(
parameter
);
}
<
T
>
void
addParameter
(
String
parameter
,
T
value
)
{
appendExceptFirst
(
", "
);
append
(
parameter
);
append
(
'='
);
if
(
value
instanceof
List
)
{
append
(
"{ "
);
List
list
=
(
List
)
value
;
StatementBuilder
flat
=
new
StatementBuilder
();
for
(
Object
o:
list
)
{
flat
.
appendExceptFirst
(
", "
);
if
(
o
instanceof
String
)
flat
.
append
(
'\"'
);
flat
.
append
(
o
.
toString
().
trim
());
if
(
o
instanceof
String
)
flat
.
append
(
'\"'
);
}
append
(
flat
);
append
(
" }"
);
}
else
{
if
(
value
instanceof
String
)
append
(
'\"'
);
append
(
value
.
toString
().
trim
());
if
(
value
instanceof
String
)
append
(
'\"'
);
}
}
}
}
\ No newline at end of file
h2/src/tools/org/h2/jaqu/Validation.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: James Moger
*/
package
org
.
h2
.
jaqu
;
import
org.h2.jaqu.TableDefinition.FieldDefinition
;
import
org.h2.jaqu.TableInspector.ColumnInspector
;
import
org.h2.jaqu.util.StringUtils
;
/**
* A Validation Remark is a result of running a model validation.
* <p>
* Each remark has a level, associated component (schema, table, column, index),
* and a message.
*
*/
public
class
Validation
{
public
static
Validation
CONSIDER
(
String
table
,
String
type
,
String
message
)
{
return
new
Validation
(
Level
.
CONSIDER
,
table
,
type
,
message
);
}
public
static
Validation
CONSIDER
(
String
table
,
ColumnInspector
col
,
String
message
)
{
return
new
Validation
(
Level
.
CONSIDER
,
table
,
col
,
message
);
}
public
static
Validation
WARN
(
String
table
,
ColumnInspector
col
,
String
message
)
{
return
new
Validation
(
Level
.
WARN
,
table
,
col
,
message
);
}
public
static
Validation
WARN
(
String
table
,
String
type
,
String
message
)
{
return
new
Validation
(
Level
.
WARN
,
table
,
type
,
message
);
}
public
static
Validation
ERROR
(
String
table
,
ColumnInspector
col
,
String
message
)
{
return
new
Validation
(
Level
.
ERROR
,
table
,
col
,
message
);
}
public
static
Validation
ERROR
(
String
table
,
String
type
,
String
message
)
{
return
new
Validation
(
Level
.
ERROR
,
table
,
type
,
message
);
}
public
static
Validation
ERROR
(
String
table
,
FieldDefinition
field
,
String
message
)
{
return
new
Validation
(
Level
.
ERROR
,
table
,
field
,
message
);
}
public
static
enum
Level
{
CONSIDER
,
WARN
,
ERROR
;
}
Level
level
;
String
table
;
String
fieldType
;
String
fieldName
;
String
message
;
private
Validation
(
Level
level
,
String
table
,
String
type
,
String
message
)
{
this
.
level
=
level
;
this
.
table
=
table
;
this
.
fieldType
=
type
;
this
.
fieldName
=
""
;
this
.
message
=
message
;
}
private
Validation
(
Level
level
,
String
table
,
FieldDefinition
field
,
String
message
)
{
this
.
level
=
level
;
this
.
table
=
table
;
this
.
fieldType
=
field
.
dataType
;
this
.
fieldName
=
field
.
columnName
;
this
.
message
=
message
;
}
private
Validation
(
Level
level
,
String
table
,
ColumnInspector
col
,
String
message
)
{
this
.
level
=
level
;
this
.
table
=
table
;
this
.
fieldType
=
col
.
type
;
this
.
fieldName
=
col
.
name
;
this
.
message
=
message
;
}
public
Validation
throwError
(
boolean
throwOnError
)
{
if
(
throwOnError
&&
isError
())
throw
new
RuntimeException
(
toString
());
return
this
;
}
public
boolean
isError
()
{
return
level
.
equals
(
Level
.
ERROR
);
}
public
String
toString
()
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
StringUtils
.
pad
(
level
.
name
(),
9
,
" "
,
true
));
sb
.
append
(
StringUtils
.
pad
(
table
,
25
,
" "
,
true
));
sb
.
append
(
StringUtils
.
pad
(
fieldName
,
20
,
" "
,
true
));
sb
.
append
(
' '
);
sb
.
append
(
message
);
return
sb
.
toString
();
}
public
String
toCSVString
()
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
level
.
name
()).
append
(
','
);
sb
.
append
(
table
).
append
(
','
);
sb
.
append
(
fieldType
).
append
(
','
);
sb
.
append
(
fieldName
).
append
(
','
);
sb
.
append
(
message
);
return
sb
.
toString
();
}
}
h2/src/tools/org/h2/jaqu/bytecode/ConstantString.java
浏览文件 @
0bedabe6
...
...
@@ -8,7 +8,7 @@ package org.h2.jaqu.bytecode;
import
org.h2.jaqu.Query
;
import
org.h2.jaqu.SQLStatement
;
import
org.h2.util.StringUtils
;
import
org.h2.
jaqu.
util.StringUtils
;
/**
* A string constant.
...
...
h2/src/tools/org/h2/jaqu/util/GenerateModels.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: James Moger
*/
package
org
.
h2
.
jaqu
.
util
;
import
java.io.BufferedWriter
;
import
java.io.File
;
import
java.io.FileWriter
;
import
java.io.IOException
;
import
java.io.PrintStream
;
import
java.io.PrintWriter
;
import
java.io.Writer
;
import
java.sql.Connection
;
import
java.sql.DriverManager
;
import
java.sql.SQLException
;
import
java.util.List
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
org.h2.jaqu.Db
;
import
org.h2.jaqu.DbInspector
;
/**
* Generates JaQu models.
*
*/
public
class
GenerateModels
{
/**
* The output stream where this tool writes to.
*/
protected
PrintStream
out
=
System
.
out
;
public
static
void
main
(
String
...
args
)
throws
SQLException
{
new
GenerateModels
().
runTool
(
args
);
}
public
void
runTool
(
String
...
args
)
throws
SQLException
{
String
url
=
null
;
String
user
=
"sa"
;
String
password
=
""
;
String
schema
=
null
;
String
table
=
null
;
String
packageName
=
""
;
String
folder
=
null
;
boolean
annotateSchema
=
true
;
boolean
trimStrings
=
false
;
for
(
int
i
=
0
;
args
!=
null
&&
i
<
args
.
length
;
i
++)
{
String
arg
=
args
[
i
];
if
(
arg
.
equals
(
"-url"
))
{
url
=
args
[++
i
];
}
else
if
(
arg
.
equals
(
"-user"
))
{
user
=
args
[++
i
];
}
else
if
(
arg
.
equals
(
"-password"
))
{
password
=
args
[++
i
];
}
else
if
(
arg
.
equals
(
"-schema"
))
{
schema
=
args
[++
i
];
}
else
if
(
arg
.
equals
(
"-table"
))
{
table
=
args
[++
i
];
}
else
if
(
arg
.
equals
(
"-package"
))
{
packageName
=
args
[++
i
];
}
else
if
(
arg
.
equals
(
"-folder"
))
{
folder
=
args
[++
i
];
}
else
if
(
arg
.
equals
(
"-annotateSchema"
))
{
try
{
annotateSchema
=
Boolean
.
parseBoolean
(
args
[++
i
]);
}
catch
(
Throwable
t
)
{
throw
new
SQLException
(
"Can not parse -annotateSchema value"
);
}
}
else
if
(
arg
.
equals
(
"-trimStrings"
))
{
try
{
trimStrings
=
Boolean
.
parseBoolean
(
args
[++
i
]);
}
catch
(
Throwable
t
)
{
throw
new
SQLException
(
"Can not parse -trimStrings value"
);
}
}
else
{
throwUnsupportedOption
(
arg
);
}
}
if
(
url
==
null
)
{
throw
new
SQLException
(
"URL not set"
);
}
execute
(
url
,
user
,
password
,
schema
,
table
,
packageName
,
folder
,
annotateSchema
,
trimStrings
);
}
/**
* Generates models from the database.
*
* @param url the database URL
* @param user the user name
* @param password the password
* @param schema the schema to read from. null for all schemas.
* @param table the table to model. null for all tables within schema.
* @param packageName the package name of the model classes.
* @param folder destination folder for model classes (package path not included)
* @param annotateSchema includes the schema in the table model annotations
* @param trimStrings automatically trim strings that exceed maxLength
*/
public
static
void
execute
(
String
url
,
String
user
,
String
password
,
String
schema
,
String
table
,
String
packageName
,
String
folder
,
boolean
annotateSchema
,
boolean
trimStrings
)
throws
SQLException
{
Connection
conn
=
null
;
try
{
org
.
h2
.
Driver
.
load
();
conn
=
DriverManager
.
getConnection
(
url
,
user
,
password
);
Db
db
=
Db
.
open
(
url
,
user
,
password
.
toCharArray
());
DbInspector
inspector
=
new
DbInspector
(
db
);
List
<
String
>
models
=
inspector
.
generateModel
(
schema
,
table
,
packageName
,
annotateSchema
,
trimStrings
);
File
parentFile
;
if
(
StringUtils
.
isNullOrEmpty
(
folder
))
parentFile
=
new
File
(
System
.
getProperty
(
"user.dir"
));
else
parentFile
=
new
File
(
folder
);
parentFile
.
mkdirs
();
Pattern
p
=
Pattern
.
compile
(
"class ([a-zA-Z0-9]+)"
);
for
(
String
model
:
models
)
{
Matcher
m
=
p
.
matcher
(
model
);
if
(
m
.
find
())
{
String
className
=
m
.
group
().
substring
(
"class"
.
length
()).
trim
();
File
classFile
=
new
File
(
parentFile
,
className
+
".java"
);
Writer
o
=
new
FileWriter
(
classFile
,
false
);
PrintWriter
writer
=
new
PrintWriter
(
new
BufferedWriter
(
o
));
writer
.
write
(
model
);
writer
.
close
();
System
.
out
.
println
(
"Generated "
+
classFile
.
getAbsolutePath
());
}
}
}
catch
(
SQLException
s
)
{
throw
s
;
}
catch
(
IOException
i
)
{
throw
new
SQLException
(
i
);
}
finally
{
JdbcUtils
.
closeSilently
(
conn
);
}
}
/**
* Throw a SQLException saying this command line option is not supported.
*
* @param option the unsupported option
* @return this method never returns normally
*/
protected
SQLException
throwUnsupportedOption
(
String
option
)
throws
SQLException
{
showUsage
();
throw
new
SQLException
(
"Unsupported option: "
+
option
);
}
protected
void
showUsage
()
{
out
.
println
(
"GenerateModels"
);
out
.
println
(
"Usage: java "
+
getClass
().
getName
());
out
.
println
();
out
.
println
(
"(*) -url jdbc:h2:~test"
);
out
.
println
(
" -user <string>"
);
out
.
println
(
" -password <string>"
);
out
.
println
(
" -schema <string>"
);
out
.
println
(
" -table <string>"
);
out
.
println
(
" -package <string>"
);
out
.
println
(
" -folder <string>"
);
out
.
println
(
" -annotateSchema <boolean>"
);
out
.
println
(
" -trimStrings <boolean>"
);
}
}
h2/src/tools/org/h2/jaqu/util/JdbcUtils.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package
org
.
h2
.
jaqu
.
util
;
import
java.sql.Connection
;
import
java.sql.DriverManager
;
import
java.sql.ResultSet
;
import
java.sql.SQLException
;
import
java.sql.Statement
;
import
java.util.Properties
;
import
javax.naming.Context
;
import
javax.sql.DataSource
;
import
javax.sql.XAConnection
;
/**
* This is a utility class with JDBC helper functions.
*/
public
class
JdbcUtils
{
private
static
final
String
[]
DRIVERS
=
{
"h2:"
,
"org.h2.Driver"
,
"Cache:"
,
"com.intersys.jdbc.CacheDriver"
,
"daffodilDB://"
,
"in.co.daffodil.db.rmi.RmiDaffodilDBDriver"
,
"daffodil"
,
"in.co.daffodil.db.jdbc.DaffodilDBDriver"
,
"db2:"
,
"COM.ibm.db2.jdbc.net.DB2Driver"
,
"derby:net:"
,
"org.apache.derby.jdbc.ClientDriver"
,
"derby://"
,
"org.apache.derby.jdbc.ClientDriver"
,
"derby:"
,
"org.apache.derby.jdbc.EmbeddedDriver"
,
"FrontBase:"
,
"com.frontbase.jdbc.FBJDriver"
,
"firebirdsql:"
,
"org.firebirdsql.jdbc.FBDriver"
,
"hsqldb:"
,
"org.hsqldb.jdbcDriver"
,
"informix-sqli:"
,
"com.informix.jdbc.IfxDriver"
,
"jtds:"
,
"net.sourceforge.jtds.jdbc.Driver"
,
"microsoft:"
,
"com.microsoft.jdbc.sqlserver.SQLServerDriver"
,
"mimer:"
,
"com.mimer.jdbc.Driver"
,
"mysql:"
,
"com.mysql.jdbc.Driver"
,
"odbc:"
,
"sun.jdbc.odbc.JdbcOdbcDriver"
,
"oracle:"
,
"oracle.jdbc.driver.OracleDriver"
,
"pervasive:"
,
"com.pervasive.jdbc.v2.Driver"
,
"pointbase:micro:"
,
"com.pointbase.me.jdbc.jdbcDriver"
,
"pointbase:"
,
"com.pointbase.jdbc.jdbcUniversalDriver"
,
"postgresql:"
,
"org.postgresql.Driver"
,
"sybase:"
,
"com.sybase.jdbc3.jdbc.SybDriver"
,
"sqlserver:"
,
"com.microsoft.sqlserver.jdbc.SQLServerDriver"
,
"teradata:"
,
"com.ncr.teradata.TeraDriver"
,
};
private
JdbcUtils
()
{
// utility class
}
/**
* Close a statement without throwing an exception.
*
* @param stat the statement or null
*/
public
static
void
closeSilently
(
Statement
stat
)
{
if
(
stat
!=
null
)
{
try
{
stat
.
close
();
}
catch
(
SQLException
e
)
{
// ignore
}
}
}
/**
* Close a connection without throwing an exception.
*
* @param conn the connection or null
*/
public
static
void
closeSilently
(
Connection
conn
)
{
if
(
conn
!=
null
)
{
try
{
conn
.
close
();
}
catch
(
SQLException
e
)
{
// ignore
}
}
}
/**
* Close a result set without throwing an exception.
*
* @param rs the result set or null
*/
public
static
void
closeSilently
(
ResultSet
rs
)
{
closeSilently
(
rs
,
false
);
}
/**
* Close a result set, and optionally its statement without throwing an
* exception.
*
* @param rs the result set or null
*/
public
static
void
closeSilently
(
ResultSet
rs
,
boolean
closeStatement
)
{
if
(
rs
!=
null
)
{
Statement
stat
=
null
;
if
(
closeStatement
)
{
try
{
stat
=
rs
.
getStatement
();
}
catch
(
SQLException
e
)
{
//ignore
}
}
try
{
rs
.
close
();
}
catch
(
SQLException
e
)
{
// ignore
}
closeSilently
(
stat
);
}
}
/**
* Close an XA connection set without throwing an exception.
*
* @param conn the XA connection or null
*/
//## Java 1.4 begin ##
public
static
void
closeSilently
(
XAConnection
conn
)
{
if
(
conn
!=
null
)
{
try
{
conn
.
close
();
}
catch
(
SQLException
e
)
{
// ignore
}
}
}
//## Java 1.4 end ##
/**
* Open a new database connection with the given settings.
*
* @param driver the driver class name
* @param url the database URL
* @param user the user name
* @param password the password
* @return the database connection
*/
public
static
Connection
getConnection
(
String
driver
,
String
url
,
String
user
,
String
password
)
throws
SQLException
{
Properties
prop
=
new
Properties
();
if
(
user
!=
null
)
{
prop
.
setProperty
(
"user"
,
user
);
}
if
(
password
!=
null
)
{
prop
.
setProperty
(
"password"
,
password
);
}
return
getConnection
(
driver
,
url
,
prop
);
}
/**
* Escape table or schema patterns used for DatabaseMetaData functions.
*
* @param pattern the pattern
* @return the escaped pattern
*/
public
static
String
escapeMetaDataPattern
(
String
pattern
)
{
if
(
pattern
==
null
||
pattern
.
length
()
==
0
)
{
return
pattern
;
}
return
StringUtils
.
replaceAll
(
pattern
,
"\\"
,
"\\\\"
);
}
/**
* Open a new database connection with the given settings.
*
* @param driver the driver class name
* @param url the database URL
* @param prop the properties containing at least the user name and password
* @return the database connection
*/
public
static
Connection
getConnection
(
String
driver
,
String
url
,
Properties
prop
)
throws
SQLException
{
if
(
StringUtils
.
isNullOrEmpty
(
driver
))
{
JdbcUtils
.
load
(
url
);
}
else
{
Class
<?>
d
=
ClassUtils
.
loadClass
(
driver
);
if
(
java
.
sql
.
Driver
.
class
.
isAssignableFrom
(
d
))
{
return
DriverManager
.
getConnection
(
url
,
prop
);
//## Java 1.4 begin ##
}
else
if
(
javax
.
naming
.
Context
.
class
.
isAssignableFrom
(
d
))
{
// JNDI context
try
{
Context
context
=
(
Context
)
d
.
newInstance
();
DataSource
ds
=
(
DataSource
)
context
.
lookup
(
url
);
String
user
=
prop
.
getProperty
(
"user"
);
String
password
=
prop
.
getProperty
(
"password"
);
if
(
StringUtils
.
isNullOrEmpty
(
user
)
&&
StringUtils
.
isNullOrEmpty
(
password
))
{
return
ds
.
getConnection
();
}
return
ds
.
getConnection
(
user
,
password
);
}
catch
(
Exception
e
)
{
throw
Message
.
convert
(
e
);
}
//## Java 1.4 end ##
}
else
{
// Don't know, but maybe it loaded a JDBC Driver
return
DriverManager
.
getConnection
(
url
,
prop
);
}
}
return
DriverManager
.
getConnection
(
url
,
prop
);
}
/**
* Get the driver class name for the given URL, or null if the URL is
* unknown.
*
* @param url the database URL
* @return the driver class name
*/
public
static
String
getDriver
(
String
url
)
{
if
(
url
.
startsWith
(
"jdbc:"
))
{
url
=
url
.
substring
(
"jdbc:"
.
length
());
for
(
int
i
=
0
;
i
<
DRIVERS
.
length
;
i
+=
2
)
{
String
prefix
=
DRIVERS
[
i
];
if
(
url
.
startsWith
(
prefix
))
{
return
DRIVERS
[
i
+
1
];
}
}
}
return
null
;
}
/**
* Load the driver class for the given URL, if the database URL is known.
*
* @param url the database URL
*/
public
static
void
load
(
String
url
)
{
String
driver
=
getDriver
(
url
);
if
(
driver
!=
null
)
{
ClassUtils
.
loadClass
(
driver
);
}
}
}
h2/src/tools/org/h2/jaqu/util/StatementBuilder.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package
org
.
h2
.
jaqu
.
util
;
/**
* A utility class to build a statement. In addition to the methods supported by
* StringBuilder, it allows to add a text only in the second iteration. This
* simplified constructs such as:
* <pre>
* StringBuilder buff = new StringBuilder();
* for (int i = 0; i < args.length; i++) {
* if (i > 0) {
* buff.append(", ");
* }
* buff.append(args[i]);
* }
* </pre>
* to
* <pre>
* StatementBuilder buff = new StatementBuilder();
* for (String s : args) {
* buff.appendExceptFirst(", ");
* buff.append(a);
* }
*</pre>
*/
public
class
StatementBuilder
{
private
final
StringBuilder
builder
=
new
StringBuilder
();
private
int
index
;
/**
* Create a new builder.
*/
public
StatementBuilder
()
{
// nothing to do
}
/**
* Create a new builder.
*
* @param string the initial string
*/
public
StatementBuilder
(
String
string
)
{
builder
.
append
(
string
);
}
/**
* Append a text.
*
* @param s the text to append
* @return itself
*/
public
StatementBuilder
append
(
String
s
)
{
builder
.
append
(
s
);
return
this
;
}
/**
* Append a character.
*
* @param c the character to append
* @return itself
*/
public
StatementBuilder
append
(
char
c
)
{
builder
.
append
(
c
);
return
this
;
}
/**
* Append a number.
*
* @param x the number to append
* @return itself
*/
public
StatementBuilder
append
(
long
x
)
{
builder
.
append
(
x
);
return
this
;
}
/**
* Reset the loop counter.
*
* @return itself
*/
public
StatementBuilder
resetCount
()
{
index
=
0
;
return
this
;
}
/**
* Append a text, but only if appendExceptFirst was never called.
*
* @param s the text to append
*/
public
void
appendOnlyFirst
(
String
s
)
{
if
(
index
==
0
)
{
builder
.
append
(
s
);
}
}
/**
* Append a text, except when this method is called the first time.
*
* @param s the text to append
*/
public
void
appendExceptFirst
(
String
s
)
{
if
(
index
++
>
0
)
{
builder
.
append
(
s
);
}
}
public
void
append
(
StatementBuilder
sb
)
{
builder
.
append
(
sb
);
}
public
void
insert
(
int
offset
,
char
c
)
{
builder
.
insert
(
offset
,
c
);
}
public
String
toString
()
{
return
builder
.
toString
();
}
/**
* Get the length.
*
* @return the length
*/
public
int
length
()
{
return
builder
.
length
();
}
}
h2/src/tools/org/h2/jaqu/util/StatementLogger.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: James Moger
*/
package
org
.
h2
.
jaqu
.
util
;
import
java.io.PrintWriter
;
import
java.text.DecimalFormat
;
import
java.util.concurrent.atomic.AtomicLong
;
/**
* Utility class to optionally log generated statements to an output stream.<br>
* Default output stream is System.out.<br>
* Statement logging is disabled by default.
* <p>
* This class also tracks the counts for generated statements by major type.
*
*/
public
class
StatementLogger
{
public
static
boolean
logStatements
=
false
;
public
static
PrintWriter
out
=
new
PrintWriter
(
System
.
out
);
public
final
static
AtomicLong
selectCount
=
new
AtomicLong
(
0
);
public
final
static
AtomicLong
createCount
=
new
AtomicLong
(
0
);
public
final
static
AtomicLong
insertCount
=
new
AtomicLong
(
0
);
public
final
static
AtomicLong
updateCount
=
new
AtomicLong
(
0
);
public
final
static
AtomicLong
mergeCount
=
new
AtomicLong
(
0
);
public
final
static
AtomicLong
deleteCount
=
new
AtomicLong
(
0
);
public
static
void
create
(
String
statement
)
{
createCount
.
incrementAndGet
();
log
(
statement
);
}
public
static
void
insert
(
String
statement
)
{
insertCount
.
incrementAndGet
();
log
(
statement
);
}
public
static
void
update
(
String
statement
)
{
updateCount
.
incrementAndGet
();
log
(
statement
);
}
public
static
void
merge
(
String
statement
)
{
mergeCount
.
incrementAndGet
();
log
(
statement
);
}
public
static
void
delete
(
String
statement
)
{
deleteCount
.
incrementAndGet
();
log
(
statement
);
}
public
static
void
select
(
String
statement
)
{
selectCount
.
incrementAndGet
();
log
(
statement
);
}
private
static
void
log
(
String
statement
)
{
if
(
logStatements
)
out
.
println
(
statement
);
}
public
static
void
printStats
()
{
out
.
println
(
"JaQu Runtime Stats"
);
out
.
println
(
"======================="
);
printStat
(
"CREATE"
,
createCount
);
printStat
(
"INSERT"
,
insertCount
);
printStat
(
"UPDATE"
,
updateCount
);
printStat
(
"MERGE"
,
mergeCount
);
printStat
(
"DELETE"
,
deleteCount
);
printStat
(
"SELECT"
,
selectCount
);
}
private
static
void
printStat
(
String
name
,
AtomicLong
value
)
{
if
(
value
.
get
()
>
0
)
{
DecimalFormat
df
=
new
DecimalFormat
(
"###,###,###,###"
);
out
.
println
(
name
+
"="
+
df
.
format
(
createCount
.
get
()));
}
}
}
\ No newline at end of file
h2/src/tools/org/h2/jaqu/util/StringUtils.java
0 → 100644
浏览文件 @
0bedabe6
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package
org
.
h2
.
jaqu
.
util
;
public
class
StringUtils
{
/**
* Replace all occurrences of the before string with the after string.
*
* @param s the string
* @param before the old text
* @param after the new text
* @return the string with the before string replaced
*/
public
static
String
replaceAll
(
String
s
,
String
before
,
String
after
)
{
int
next
=
s
.
indexOf
(
before
);
if
(
next
<
0
)
{
return
s
;
}
StringBuilder
buff
=
new
StringBuilder
(
s
.
length
()
-
before
.
length
()
+
after
.
length
());
int
index
=
0
;
while
(
true
)
{
buff
.
append
(
s
.
substring
(
index
,
next
)).
append
(
after
);
index
=
next
+
before
.
length
();
next
=
s
.
indexOf
(
before
,
index
);
if
(
next
<
0
)
{
buff
.
append
(
s
.
substring
(
index
));
break
;
}
}
return
buff
.
toString
();
}
/**
* Check if a String is null or empty (the length is null).
*
* @param s the string to check
* @return true if it is null or empty
*/
public
static
boolean
isNullOrEmpty
(
String
s
)
{
return
s
==
null
||
s
.
length
()
==
0
;
}
/**
* Convert a string to a Java literal using the correct escape sequences.
* The literal is not enclosed in double quotes. The result can be used in
* properties files or in Java source code.
*
* @param s the text to convert
* @return the Java representation
*/
public
static
String
javaEncode
(
String
s
)
{
int
length
=
s
.
length
();
StringBuilder
buff
=
new
StringBuilder
(
length
);
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
char
c
=
s
.
charAt
(
i
);
switch
(
c
)
{
// case '\b':
// // BS backspace
// // not supported in properties files
// buff.append("\\b");
// break;
case
'\t'
:
// HT horizontal tab
buff
.
append
(
"\\t"
);
break
;
case
'\n'
:
// LF linefeed
buff
.
append
(
"\\n"
);
break
;
case
'\f'
:
// FF form feed
buff
.
append
(
"\\f"
);
break
;
case
'\r'
:
// CR carriage return
buff
.
append
(
"\\r"
);
break
;
case
'"'
:
// double quote
buff
.
append
(
"\\\""
);
break
;
case
'\\'
:
// backslash
buff
.
append
(
"\\\\"
);
break
;
default
:
int
ch
=
c
&
0xffff
;
if
(
ch
>=
' '
&&
(
ch
<
0x80
))
{
buff
.
append
(
c
);
// not supported in properties files
// } else if(ch < 0xff) {
// buff.append("\\");
// // make sure it's three characters (0x200 is octal 1000)
// buff.append(Integer.toOctalString(0x200 |
// ch).substring(1));
}
else
{
buff
.
append
(
"\\u"
);
// make sure it's four characters
buff
.
append
(
Integer
.
toHexString
(
0x10000
|
ch
).
substring
(
1
));
}
}
}
return
buff
.
toString
();
}
/**
* Pad a string. This method is used for the SQL function RPAD and LPAD.
*
* @param string the original string
* @param n the target length
* @param padding the padding string
* @param right true if the padding should be appended at the end
* @return the padded string
*/
public
static
String
pad
(
String
string
,
int
n
,
String
padding
,
boolean
right
)
{
if
(
n
<
0
)
{
n
=
0
;
}
if
(
n
<
string
.
length
())
{
return
string
.
substring
(
0
,
n
);
}
else
if
(
n
==
string
.
length
())
{
return
string
;
}
char
paddingChar
;
if
(
padding
==
null
||
padding
.
length
()
==
0
)
{
paddingChar
=
' '
;
}
else
{
paddingChar
=
padding
.
charAt
(
0
);
}
StringBuilder
buff
=
new
StringBuilder
(
n
);
n
-=
string
.
length
();
if
(
right
)
{
buff
.
append
(
string
);
}
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
buff
.
append
(
paddingChar
);
}
if
(!
right
)
{
buff
.
append
(
string
);
}
return
buff
.
toString
();
}
/**
* Convert a string to a SQL literal. Null is converted to NULL. The text is
* enclosed in single quotes. If there are any special characters, the method
* STRINGDECODE is used.
*
* @param s the text to convert.
* @return the SQL literal
*/
public
static
String
quoteStringSQL
(
String
s
)
{
if
(
s
==
null
)
{
return
"NULL"
;
}
int
length
=
s
.
length
();
StringBuilder
buff
=
new
StringBuilder
(
length
+
2
);
buff
.
append
(
'\''
);
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
char
c
=
s
.
charAt
(
i
);
if
(
c
==
'\''
)
{
buff
.
append
(
c
);
}
else
if
(
c
<
' '
||
c
>
127
)
{
// need to start from the beginning because maybe there was a \
// that was not quoted
return
"STRINGDECODE("
+
quoteStringSQL
(
javaEncode
(
s
))
+
")"
;
}
buff
.
append
(
c
);
}
buff
.
append
(
'\''
);
return
buff
.
toString
();
}
}
h2/src/tools/org/h2/jaqu/util/Utils.java
浏览文件 @
0bedabe6
...
...
@@ -7,19 +7,24 @@
package
org
.
h2
.
jaqu
.
util
;
//## Java 1.5 begin ##
import
java.io.IOException
;
import
java.io.Reader
;
import
java.io.StringWriter
;
import
java.lang.reflect.Constructor
;
import
java.math.BigDecimal
;
import
java.math.BigInteger
;
import
java.sql.Clob
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.IdentityHashMap
;
import
java.util.List
;
import
java.util.Map
;
import
org.h2.util.IOUtils
;
//## Java 1.5 end ##
import
java.util.Set
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.atomic.AtomicLong
;
/**
* Generic utility methods.
...
...
@@ -27,13 +32,31 @@ import org.h2.util.IOUtils;
public
class
Utils
{
//## Java 1.5 begin ##
private
static
volatile
long
counter
;
private
static
final
AtomicLong
counter
=
new
AtomicLong
(
0
)
;
private
static
final
boolean
MAKE_ACCESSIBLE
=
true
;
private
static
final
int
BUFFER_BLOCK_SIZE
=
4
*
1024
;
public
static
<
T
>
ArrayList
<
T
>
newArrayList
()
{
return
new
ArrayList
<
T
>();
}
public
static
<
T
>
ArrayList
<
T
>
newArrayList
(
Collection
<
T
>
c
)
{
return
new
ArrayList
<
T
>(
c
);
}
public
static
<
T
>
HashSet
<
T
>
newHashSet
()
{
return
new
HashSet
<
T
>();
}
public
static
<
T
>
HashSet
<
T
>
newHashSet
(
Collection
<
T
>
list
)
{
return
new
HashSet
<
T
>(
list
);
}
public
static
<
T
>
Set
<
T
>
newConcurrentHashSet
()
{
return
Collections
.
newSetFromMap
(
new
ConcurrentHashMap
<
T
,
Boolean
>());
}
public
static
<
A
,
B
>
HashMap
<
A
,
B
>
newHashMap
()
{
return
new
HashMap
<
A
,
B
>();
...
...
@@ -52,35 +75,35 @@ public class Utils {
public
static
<
T
>
T
newObject
(
Class
<
T
>
clazz
)
{
// must create new instances
if
(
clazz
==
Integer
.
class
)
{
return
(
T
)
new
Integer
((
int
)
counter
++
);
return
(
T
)
new
Integer
((
int
)
counter
.
incrementAndGet
()
);
}
else
if
(
clazz
==
String
.
class
)
{
return
(
T
)
(
""
+
counter
++
);
return
(
T
)
(
""
+
counter
.
incrementAndGet
()
);
}
else
if
(
clazz
==
Long
.
class
)
{
return
(
T
)
new
Long
(
counter
++
);
return
(
T
)
new
Long
(
counter
.
incrementAndGet
()
);
}
else
if
(
clazz
==
Short
.
class
)
{
return
(
T
)
new
Short
((
short
)
counter
++
);
return
(
T
)
new
Short
((
short
)
counter
.
incrementAndGet
()
);
}
else
if
(
clazz
==
Byte
.
class
)
{
return
(
T
)
new
Byte
((
byte
)
counter
++
);
return
(
T
)
new
Byte
((
byte
)
counter
.
incrementAndGet
()
);
}
else
if
(
clazz
==
Float
.
class
)
{
return
(
T
)
new
Float
(
counter
++
);
return
(
T
)
new
Float
(
counter
.
incrementAndGet
()
);
}
else
if
(
clazz
==
Double
.
class
)
{
return
(
T
)
new
Double
(
counter
++
);
return
(
T
)
new
Double
(
counter
.
incrementAndGet
()
);
}
else
if
(
clazz
==
Boolean
.
class
)
{
return
(
T
)
new
Boolean
(
false
);
}
else
if
(
clazz
==
BigDecimal
.
class
)
{
return
(
T
)
new
BigDecimal
(
counter
++
);
return
(
T
)
new
BigDecimal
(
counter
.
incrementAndGet
()
);
}
else
if
(
clazz
==
BigInteger
.
class
)
{
return
(
T
)
new
BigInteger
(
""
+
counter
++
);
return
(
T
)
new
BigInteger
(
""
+
counter
.
incrementAndGet
()
);
}
else
if
(
clazz
==
java
.
sql
.
Date
.
class
)
{
return
(
T
)
new
java
.
sql
.
Date
(
counter
++
);
return
(
T
)
new
java
.
sql
.
Date
(
counter
.
incrementAndGet
()
);
}
else
if
(
clazz
==
java
.
sql
.
Time
.
class
)
{
return
(
T
)
new
java
.
sql
.
Time
(
counter
++
);
return
(
T
)
new
java
.
sql
.
Time
(
counter
.
incrementAndGet
()
);
}
else
if
(
clazz
==
java
.
sql
.
Timestamp
.
class
)
{
return
(
T
)
new
java
.
sql
.
Timestamp
(
counter
++
);
return
(
T
)
new
java
.
sql
.
Timestamp
(
counter
.
incrementAndGet
()
);
}
else
if
(
clazz
==
java
.
util
.
Date
.
class
)
{
return
(
T
)
new
java
.
util
.
Date
(
counter
++
);
return
(
T
)
new
java
.
util
.
Date
(
counter
.
incrementAndGet
()
);
}
else
if
(
clazz
==
List
.
class
)
{
return
(
T
)
new
ArrayList
();
return
(
T
)
newArrayList
();
}
try
{
return
clazz
.
newInstance
();
...
...
@@ -137,7 +160,7 @@ public class Utils {
Clob
c
=
(
Clob
)
o
;
try
{
Reader
r
=
c
.
getCharacterStream
();
return
IOUtils
.
readStringAndClose
(
r
,
-
1
);
return
readStringAndClose
(
r
,
-
1
);
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
"Error converting CLOB to String: "
+
e
.
toString
(),
e
);
}
...
...
@@ -146,7 +169,11 @@ public class Utils {
}
if
(
Number
.
class
.
isAssignableFrom
(
currentType
))
{
Number
n
=
(
Number
)
o
;
if
(
targetType
==
Integer
.
class
)
{
if
(
targetType
==
Byte
.
class
)
{
return
n
.
byteValue
();
}
else
if
(
targetType
==
Short
.
class
)
{
return
n
.
shortValue
();
}
else
if
(
targetType
==
Integer
.
class
)
{
return
n
.
intValue
();
}
else
if
(
targetType
==
Long
.
class
)
{
return
n
.
longValue
();
...
...
@@ -159,6 +186,37 @@ public class Utils {
throw
new
RuntimeException
(
"Can not convert the value "
+
o
+
" from "
+
currentType
+
" to "
+
targetType
);
}
/**
* Read a number of characters from a reader and close it.
*
* @param in the reader
* @param length the maximum number of characters to read, or -1 to read
* until the end of file
* @return the string read
*/
public
static
String
readStringAndClose
(
Reader
in
,
int
length
)
throws
IOException
{
try
{
if
(
length
<=
0
)
{
length
=
Integer
.
MAX_VALUE
;
}
int
block
=
Math
.
min
(
BUFFER_BLOCK_SIZE
,
length
);
StringWriter
out
=
new
StringWriter
(
length
==
Integer
.
MAX_VALUE
?
block
:
length
);
char
[]
buff
=
new
char
[
block
];
while
(
length
>
0
)
{
int
len
=
Math
.
min
(
block
,
length
);
len
=
in
.
read
(
buff
,
0
,
len
);
if
(
len
<
0
)
{
break
;
}
out
.
write
(
buff
,
0
,
len
);
length
-=
len
;
}
return
out
.
toString
();
}
finally
{
in
.
close
();
}
}
//## Java 1.5 end ##
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论