Retrieve Moxie Toolkit\r
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
-->\r
- <property name="moxie.version" value="0.7.0-SNAPSHOT" />\r
+ <property name="moxie.version" value="0.7.0" />\r
<property name="moxie.url" value="http://gitblit.github.com/moxie/maven" />\r
<property name="moxie.jar" value="moxie-toolkit-${moxie.version}.jar" />\r
<property name="moxie.dir" value="${user.home}/.moxie" />\r
<property name="gc.url" value="http://code.google.com/p/iciql/downloads/detail?name=" />\r
<property name="releaselog" value="${basedir}/releases.moxie" />\r
\r
- <mx:doc googleplusone="true" injectprettify="true" prettifyTheme="hemisu-dark" minify="true" \r
+ <mx:doc googleplusone="true" prettifyTheme="googlecode" minify="true" \r
templateDir="${project.siteSourceDirectory}/templates"\r
customless="custom.less" rssFeed="rss.xml" atomFeed="atom.xml">\r
\r
<favicon file="iciql-favicon.png" />\r
\r
<load token="%DBPERFORMANCE%" file="${project.outputDirectory}/performance_db.txt" />\r
- <nomarkdown startToken="%BEGINCODE%" endToken="%ENDCODE%" prettify="true" lang="java" />\r
<regex searchPattern="\b(issue)(\s*[#]?|-){0,1}(\d+)\b"\r
replacePattern="<a href='${project.issuesUrl}/detail?id=$3'>issue $3</a>" />\r
\r
View models may be specified using the IQView annotation or Iciql.define(). Views can either be created automatically as part of a query of the view OR views may be constructed from a fluent statement.
''
- Support inheritance of IQVersion for DbUpgrader implementations (issue 10)
+ - Added @IQConstraintForeignKey annotation (issue 13)
+ - Added MS SQL Server dialect (issue 14)
}
#
// GLOBAL VALUES\r
// --------------------------------------------------\r
@standardGray: #ccc;\r
-@cornflower: #99cbff;\r
+@iciql: #95C7F9;\r
@white: #fff;\r
\r
// Dropdown\r
// -------------------------\r
-@dropdownLinkBackgroundHover: @cornflower;\r
+@dropdownLinkBackgroundHover: @iciql;\r
\r
// Navbar\r
// -------------------------\r
-@navbarHeight: 55px;\r
-@navbarBackground: @cornflower;\r
-@navbarBackgroundHighlight: @cornflower;\r
+@navbarHeight: 50px;\r
+@navbarBackground: @iciql;\r
+@navbarBackgroundHighlight: @iciql;\r
@navbarText: @white;\r
@navbarLinkColor: @white;\r
@navbarLinkColorHover: @white;\r
.navbar {\r
.brand {\r
@elementHeight: 48px;\r
- padding: 7px;\r
+ padding: 5px;\r
}\r
}\r
\r
.navbar .nav > li > a {\r
- font-size: @baseFontSize + 2; \r
+ font-size: @baseFontSize + 1; \r
text-shadow: 0 1px 0 #6b94df;\r
}\r
\r
+\r
+.navbar .nav > li > a:hover {\r
+ text-shadow: 0 0 1em white;\r
+}\r
+.navbar .nav > .active > a, \r
+ .navbar .nav > .active > a:hover,\r
+ .navbar .nav > .active > a:focus {\r
+ box-shadow: none;\r
+ text-decoration: underline;\r
+}\r
+\r
+.dropdown-submenu > a:after {\r
+ margin-right: -5px;\r
+}\r
+\r
body { padding-top: @navbarHeight + 15 } /* 60px to make the container go all the way to the bottom of the topbar */\r
footer { margin-top: 25px; padding: 15px 0 16px; border-top: 1px solid #E5E5E5; }\r
\r
## Select Statements\r
\r
-%BEGINCODE%\r
+---JAVA---\r
// select * from products\r
List<Product> allProducts = db.from(p).select();\r
\r
category = p.category;\r
price = p.unitPrice;\r
}});\r
-%ENDCODE%\r
+---JAVA---\r
\r
## Insert Statements\r
\r
-%BEGINCODE%\r
+---JAVA---\r
// single record insertion\r
db.insert(singleProduct);\r
\r
\r
// batch insertion with primary key retrieval\r
List<Long> myKeys = db.insertAllAndGetKeys(list);\r
-%ENDCODE%\r
+---JAVA---\r
\r
## Update Statements\r
\r
-%BEGINCODE%\r
+---JAVA---\r
// single record update\r
db.update(singleProduct);\r
\r
// reusable, parameterized update query\r
String q = db.from(p).set(p.productName).toParameter().where(p.productId).is(1).toSQL();\r
db.executeUpdate(q, "Lettuce");\r
-\r
-%ENDCODE%\r
+---JAVA---\r
\r
## Merge Statements\r
Merge statements currently generate the [H2 merge syntax](http://h2database.com/html/grammar.html#merge).\r
\r
-%BEGINCODE%\r
+---JAVA---\r
Product pChang = db.from(p).where(p.productName).is("Chang").selectFirst();\r
pChang.unitPrice = 19.5;\r
pChang.unitsInStock = 16;\r
db.merge(pChang);\r
-%ENDCODE%\r
+---JAVA---\r
\r
## Delete Statements\r
\r
-%BEGINCODE%\r
+---JAVA---\r
// single record deletion\r
db.delete(singleProduct); \r
\r
\r
// delete query\r
db.from(p).where(p.productId).atLeast(10).delete();\r
-\r
-%ENDCODE%\r
+---JAVA---\r
\r
## Inner Join Statements\r
\r
-%BEGINCODE%\r
+---JAVA---\r
final Customer c = new Customer();\r
final Order o = new Order();\r
\r
where(o.total).greaterThan(new BigDecimal("500.00")).\r
groupBy(c.customerId).select();\r
\r
-\r
List<CustOrder> orders =\r
db.from(c).\r
innerJoin(o).on(c.customerId).is(o.customerId).\r
orderId = o.orderId;\r
total = o.total;\r
}});\r
-%ENDCODE%\r
+---JAVA---\r
\r
## View Statements\r
\r
-%BEGINCODE%\r
+---JAVA---\r
// the view named "ProductView" is created from the "Products" table\r
@IQView(viewTableName = "Products")\r
public class ProductView {\r
\r
// now drop the view from the database\r
db.dropView(ProductViewInherited.class);\r
-\r
-%ENDCODE%\r
+---JAVA---\r
\r
## Dynamic Queries\r
\r
Dynamic queries skip all field type checking and, depending on which approach you use, may skip model class/table name checking too.\r
\r
-%BEGINCODE%\r
+---JAVA---\r
// where fragment with object parameters\r
List<Product> restock = db.from(p).where("unitsInStock=? and productName like ? order by productId", 0, "Chef%").select();\r
\r
ResultSet rs = db.executeQuery("select * from products");\r
List<Product> allProducts = db.buildObjects(Product.class, rs);\r
JdbcUtils.closeSilently(rs, true);\r
-\r
-%ENDCODE%
\ No newline at end of file
+---JAVA---\r
</tr>\r
<tr>\r
<td>\r
-%BEGINCODE%\r
+---JAVA---\r
Product p = new Product();\r
List<Product> restock = db.from(p).where(p.unitsInStock).is(0).select();\r
List<Product> all = db.executeQuery(Product.class, "select * from products");\r
-%ENDCODE%\r
+---JAVA---\r
\r
</td><td>\r
<br/>\r
<tr><td>savepoints</td><td>bulk operations (insert, update, delete) use savepoints with rollback in the event of failure</td><td>--</td></tr>\r
<tr><th colspan="3">syntax and api</th></tr>\r
<tr><td>VIEWs</td><td>create readonly views either from a class definition or from a fluent statement</td><td>--</td></tr>\r
+<tr><td>Foreign Key Constraints</td><td>model classes may be annotated with foreign key constraints</td><td>--</td></tr>\r
<tr><td>dynamic queries</td><td>methods and where clauses for dynamic queries that build iciql objects</td><td>--</td></tr>\r
<tr><td>DROP</td><td>syntax to drop a table or view</td><td></td></tr>\r
<tr><td>BETWEEN</td><td>syntax for specifying a BETWEEN x AND y clause</td><td>--</td></tr>\r
1. specifying the default value string within your annotation<br/>\r
**NOTE:**<br/>\r
The annotated default value always takes priority over a field default value.\r
-%BEGINCODE%\r
+---JAVA---\r
// notice the single ticks!\r
@IQColumn(defaultValue="'2000-01-01 00:00:00'")\r
Date myDate;\r
-%ENDCODE%\r
+---JAVA---\r
\r
2. setting a default value on the field<br/>\r
**NOTE:**<br/>\r
Primitive types have an implicit default value of *0* or *false*.\r
-%BEGINCODE%\r
+---JAVA---\r
@IQColumn\r
Date myDate = new Date(100, 0, 1);\r
\r
@IQColumn\r
int myId;\r
-%ENDCODE%\r
+---JAVA---\r
\r
If you want to specify a database-specific variable or function as your default value (e.g. CURRENT_TIMESTAMP) you must do that within the annotation. Also note that the *IQColumn.defaultValue* must be a well-formatted SQL DEFAULT expression whereas object defaults will be automatically converted to an SQL DEFAULT expression.\r
\r
### Special Case: primitive autoincrement fields and 0\r
-%BEGINCODE%\r
+---JAVA---\r
@IQColumn(autoIncrement = true)\r
int myId;\r
-%ENDCODE%\r
+---JAVA---\r
\r
Because primitive types have implicit default values, this field will be excluded from an INSERT statement if its value is 0. Iciql can not differentiate an implicit/uninitialized 0 from a explicitly assigned 0.\r
\r
### Example Annotated Model\r
-%BEGINCODE%\r
+---JAVA---\r
import com.iciql.Iciql.EnumType;\r
import com.iciql.Iciql.IQColumn;\r
import com.iciql.Iciql.IQEnum;\r
// default constructor\r
}\r
}\r
-%ENDCODE%\r
+---JAVA---\r
+\r
+### Foreign Keys\r
+\r
+---JAVA---\r
+@IQTable(name = "AnnotatedProduct", primaryKey = "id")\r
+@IQIndexes({ @IQIndex({ "name", "cat" }), @IQIndex(name = "nameidx", type = IndexType.HASH, value = "name") })\r
+@IQContraintForeignKey(\r
+ foreignColumns= { "cat" }, \r
+ referenceName = "AnnotatedCategory",\r
+ referenceColumns = { "categ" },\r
+ deleteType = ConstraintDeleteType.CASCADE\r
+)\r
+public class ProductAnnotationOnlyWithForeignKey {\r
+\r
+ public String unmappedField;\r
+\r
+ @IQColumn(name = "id", autoIncrement = true)\r
+ public Long productId;\r
+\r
+ @IQColumn(name = "cat", length = 15, trim = true)\r
+ public String category;\r
+\r
+ @IQColumn(name = "name", length = 50)\r
+ public String productName;\r
+\r
+ @SuppressWarnings("unused")\r
+ @IQColumn\r
+ private Double unitPrice;\r
+\r
+ @IQColumn\r
+ private Integer unitsInStock;\r
+}\r
+---JAVA---\r
+\r
+### Views with Field Constraints\r
+---JAVA---\r
+@IQView(name = "AnnotatedProductView", tableName = "AnnotatedProduct")\r
+public class ProductView {\r
+\r
+ public String unmappedField;\r
+\r
+ @IQColumn(name = "id", autoIncrement = true)\r
+ @IQConstraint("this <= 7 AND this > 2")\r
+ public Long productId; \r
+ @IQColumn(name = "name")\r
+ public String productName;\r
+\r
+ public String toString() {\r
+ return productName + " (" + productId + ")";\r
+ }\r
+}\r
+---JAVA---\r
\r
## Interface Configuration\r
Alternatively, you may map your model classes using the interface approach by implementing the `com.iciql.Iciql` interface.\r
1. specifying the default value string within your *defineIQ()* method<br/>\r
**NOTE:**<br/>\r
The defineIQ() value always takes priority over a field default value.\r
-%BEGINCODE%\r
+---JAVA---\r
Date myDate;\r
\r
public void defineIQ() {\r
// notice the single ticks!\r
Define.defaultValue(myDate, "'2000-01-01 00:00:00'");\r
}\r
-%ENDCODE%\r
+---JAVA---\r
\r
2. setting a default value on the field<br/>\r
**NOTE:**<br/>\r
Primitive types have an implicit default value of *0* or *false*.\r
-%BEGINCODE%\r
+---JAVA---\r
Date myDate = new Date(100, 0, 1);\r
\r
int myId;\r
-%ENDCODE%\r
+---JAVA---\r
\r
### Example Interface Model\r
-%BEGINCODE%\r
+---JAVA---\r
import com.iciql.Iciql;\r
import com.iciql.Iciql.IQIgnore;\r
\r
com.iciql.Define.index(productName, category);\r
}\r
}\r
-%ENDCODE%\r
-\r
+---JAVA---\r
\r
## POJO (Plain Old Java Object) Configuration\r
\r
\r
**NOTE:**<br/>\r
Primitive types have an implicit default value of *0* or *false*.\r
-%BEGINCODE%\r
+---JAVA---\r
Date myDate = new Date(100, 0, 1);\r
\r
int myId;\r
-%ENDCODE%\r
+---JAVA---\r
\r
### Example POJO Model\r
-%BEGINCODE%\r
+---JAVA---\r
import com.iciql.Iciql.IQIgnore;\r
\r
public class Product {\r
public Product() {\r
} \r
}\r
-%ENDCODE%
\ No newline at end of file
+---JAVA---
\ No newline at end of file
Iciql can validate your model classes against your database to ensure that your models are optimally defined and are consistent with the current table and index definitions.\r
\r
Each `com.iciql.ValidationRemark` returned by the validation has an associated level from the following enum:\r
-%BEGINCODE%\r
+---JAVA---\r
public static enum Level {\r
CONSIDER, WARN, ERROR;\r
}\r
-%ENDCODE%\r
+---JAVA---\r
\r
A typical validation may output recommendations for adjusting a model field annotation such as setting the *maxLength* of a string to match the length of its linked VARCHAR column.\r
\r
### Sample Model Validation using JUnit 4\r
-%BEGINCODE%\r
+---JAVA---\r
import static org.junit.Assert.assertTrue;\r
\r
import java.sql.SQLException;\r
System.out.println(message);\r
}\r
}\r
-%ENDCODE%
\ No newline at end of file
+---JAVA---
\ No newline at end of file
\r
You compose your statements using the builder pattern where each method call returns an object that is used to specify the next part of the statement. Through clever usage of generics, pioneered by the original JaQu project, compile-time safety flows through the statement.\r
\r
-%BEGINCODE%\r
+---JAVA---\r
Db db = Db.open("jdbc:h2:mem:", "sa", "sa");\r
db.insertAll(Product.getList());\r
db.insertAll(Customer.getList());\r
where(p.productId).is(product.productId).update();\r
}\r
db.close();\r
-%ENDCODE%\r
+---JAVA---\r
\r
Please see the [examples](examples.html) page for more code samples. \r
\r
\r
#### Where String Fragment Approach\r
This approach is a mixture of iciql and jdbc. It uses the traditional prepared statement *field=?* tokens with iciql compile-time model class type checking. There is no field token type-safety checking.\r
-%BEGINCODE%\r
+---JAVA---\r
List<Product> restock = db.from(p).where("unitsInStock=? and productName like ? order by productId", 0, "Chef%").select();\r
-%ENDCODE%\r
+---JAVA---\r
\r
#### Db.executeQuery Approaches\r
There may be times when the hybrid approach is still too restrictive and you'd prefer to write straight SQL. You can do that too and use iciql to build objects from your ResultSet, but be careful:\r
1. Make sure to _select *_ in your query otherwise db.buildObjects() will throw a RuntimeException\r
2. There is no model class type checking nor field type checking. \r
\r
-%BEGINCODE%\r
+---JAVA---\r
List<Product> allProducts = db.executeQuery(Product.class, "select * from products");\r
List<Product> restock = db.executeQuery(Product.class, "select * from products where unitsInStock=?", 0);\r
\r
// parameterized query which can be cached and re-used later\r
String q = db.from(p).where(p.unitsInStock).isParameter().toSQL();\r
List<Product> restock = db.executeQuery(Product.class, q, 0);\r
-\r
-%ENDCODE% \r
+---JAVA---\r
\r
Or if you want access to the raw *ResultSet* before building your model object instances...\r
\r
-%BEGINCODE%\r
+---JAVA---\r
ResultSet rs = db.executeQuery("select * from products");\r
List<Product> allProducts = db.buildObjects(Product.class, rs);\r
// This method ensures the creating statement is closed\r
JdbcUtils.closeSilently(rs, true);\r
-%ENDCODE% \r
+---JAVA---\r
+\r
+### Read-only Views\r
+\r
+View model classes can inherit their field definitions from a parent table model class.\r
+\r
+---JAVA---\r
+@IQView(name = "AnnotatedProductViewInherited", inheritColumns = true)\r
+public class ProductViewFromQuery extends ProductAnnotationOnly {\r
+\r
+ public String unmappedField;\r
+\r
+ @IQColumn(name = "id")\r
+ public Long productId;\r
+\r
+ public String toString() {\r
+ return productName + " (" + productId + ")";\r
+ }\r
+}\r
+---JAVA---\r
+\r
+You can then create or replace the VIEW in the database using a fluent syntax.\r
+\r
+---JAVA---\r
+// create view from query\r
+ProductAnnotationOnly product = new ProductAnnotationOnly();\r
+db.from(product).where(product.productId).exceeds(2L)\r
+ .and(product.productId).atMost(7L).createView(ProductViewFromQuery.class);\r
+ \r
+// select from the created view\r
+ProductViewFromQuery view = new ProductViewFromQuery();\r
+List<ProductViewFromQuery> products = db.from(view).select();\r
+ \r
+// replace the view\r
+db.from(product).where(product.productId).exceeds(3L)\r
+ .and(product.productId).atMost(8L).replaceView(ProductViewFromQuery.class);\r
+ \r
+// select from the replaced view\r
+products = db.from(view).select();\r
+---JAVA---\r
\r
### Natural Syntax\r
\r
A proof-of-concept decompiler is included, but is incomplete.\r
\r
The proposed syntax is:\r
-%BEGINCODE%\r
+---JAVA---\r
long count = db.from(co).\r
where(new Filter() { public boolean where() {\r
return co.id == x\r
&& co.time.before(java.sql.Time.valueOf("23:23:23"));\r
}\r
}).selectCount();\r
-%ENDCODE%\r
+---JAVA---\r
\r
### JDBC Statements, ResultSets, and Exception Handling\r
\r
Iciql provides a mechanism to log generated statements and warnings to the console, to SLF4J, or to your own logging framework. Exceptions are not logged using this mechanism; exceptions are wrapped and rethrown as `IciqlException`, which is a RuntimeException.\r
\r
#### Console Logging\r
-%BEGINCODE%\r
+---JAVA---\r
IciqlLogger.activeConsoleLogger();\r
IciqlLogger.deactiveConsoleLogger();\r
-%ENDCODE%\r
+---JAVA---\r
\r
#### SLF4J Logging\r
-%BEGINCODE%\r
+---JAVA---\r
Slf4jIciqlListener slf4j = new Slf4jIciqlListener();\r
slf4j.setLevel(StatementType.CREATE, Level.WARN);\r
slf4j.setLevel(StatementType.DELETE, Level.WARN);\r
slf4j.setLevel(StatementType.MERGE, Level.OFF);\r
IciqlLogger.registerListener(slf4j);\r
IciqlLogger.unregisterListener(slf4j);\r
-%ENDCODE%\r
+---JAVA---\r
\r
#### Custom Logging\r
-%BEGINCODE%\r
+---JAVA---\r
IciqlListener custom = new IciqlListener() {\r
public void logIciql(StatementType type, String statement) {\r
// do log\r
};\r
IciqlLogger.registerListener(custom);\r
IciqlLogger.unregisterListener(custom);\r
-%ENDCODE%\r
+---JAVA---\r
\r
## Understanding Aliases and Model Classes\r
Consider the following example:\r
-%BEGINCODE%\r
+---JAVA---\r
Product p = new Product();\r
List<Product> restock = db.from(p).where(p.unitsInStock).is(0).select();\r
-%ENDCODE%\r
+---JAVA---\r
\r
The Product model class instance named **p** is an *alias* object. An *alias* is simply an instance of your model class that is only used to build the compile-time/runtime representation of your table.\r
\r
\r
Depending on your design, you might consider using a [ThreadLocal](http://download.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html) variable if you do not want to keep instantiating *alias* instances. A utility function is included for easily creating ThreadLocal variables.\r
\r
-%BEGINCODE%\r
+---JAVA---\r
final ThreadLocal<Product> p = Utils.newThreadLocal(Product.class);\r
db.from(p.get()).select();\r
-%ENDCODE%\r
+---JAVA---\r
\r
## Best Practices\r
\r
<table class="table">\r
<tr><th>Not Thread-Safe</th><th>Thread-Safe</th></tr>\r
<tr><td>\r
-%BEGINCODE%\r
+---JAVA---\r
final Product p = new Product();\r
for (int i = 0; i < 5; i++) {\r
Thread thread = new Thread(new Runnable() {\r
}, "Thread-" + i);\r
thread.start();\r
}\r
-%ENDCODE%\r
+---JAVA---\r
\r
</td><td>\r
-%BEGINCODE%\r
+---JAVA---\r
final ThreadLocal<Product> p = Utils.newThreadLocal(Product.class);\r
for (int i = 0; i < 5; i++) {\r
Thread thread = new Thread(new Runnable() {\r
}, "Thread-" + i);\r
thread.start();\r
}\r
-%ENDCODE%\r
+---JAVA---\r
\r
</td></tr>\r
</table>
\ No newline at end of file