diff options
author | James Moger <james.moger@gmail.com> | 2013-03-25 22:49:16 -0400 |
---|---|---|
committer | James Moger <james.moger@gmail.com> | 2013-03-25 22:49:16 -0400 |
commit | 49e3882ae6552a05fd2cd56e7ecd560d3f795ece (patch) | |
tree | 566b85579d00a606fb742793ecd5bf89aaf3895e /src | |
parent | ccd790f11fe1d3b6509bec0b6a9a99a341237455 (diff) | |
download | iciql-49e3882ae6552a05fd2cd56e7ecd560d3f795ece.tar.gz iciql-49e3882ae6552a05fd2cd56e7ecd560d3f795ece.zip |
Documentation
Diffstat (limited to 'src')
-rw-r--r-- | src/site/custom.less | 29 | ||||
-rw-r--r-- | src/site/examples.mkd | 37 | ||||
-rw-r--r-- | src/site/index.mkd | 4 | ||||
-rw-r--r-- | src/site/jaqu_comparison.mkd | 1 | ||||
-rw-r--r-- | src/site/model_classes.mkd | 89 | ||||
-rw-r--r-- | src/site/tools.mkd | 8 | ||||
-rw-r--r-- | src/site/usage.mkd | 88 |
7 files changed, 178 insertions, 78 deletions
diff --git a/src/site/custom.less b/src/site/custom.less index f7130d4..b9c21a8 100644 --- a/src/site/custom.less +++ b/src/site/custom.less @@ -2,18 +2,18 @@ // GLOBAL VALUES
// --------------------------------------------------
@standardGray: #ccc;
-@cornflower: #99cbff;
+@iciql: #95C7F9;
@white: #fff;
// Dropdown
// -------------------------
-@dropdownLinkBackgroundHover: @cornflower;
+@dropdownLinkBackgroundHover: @iciql;
// Navbar
// -------------------------
-@navbarHeight: 55px;
-@navbarBackground: @cornflower;
-@navbarBackgroundHighlight: @cornflower;
+@navbarHeight: 50px;
+@navbarBackground: @iciql;
+@navbarBackgroundHighlight: @iciql;
@navbarText: @white;
@navbarLinkColor: @white;
@navbarLinkColorHover: @white;
@@ -24,15 +24,30 @@ .navbar {
.brand {
@elementHeight: 48px;
- padding: 7px;
+ padding: 5px;
}
}
.navbar .nav > li > a {
- font-size: @baseFontSize + 2;
+ font-size: @baseFontSize + 1;
text-shadow: 0 1px 0 #6b94df;
}
+
+.navbar .nav > li > a:hover {
+ text-shadow: 0 0 1em white;
+}
+.navbar .nav > .active > a,
+ .navbar .nav > .active > a:hover,
+ .navbar .nav > .active > a:focus {
+ box-shadow: none;
+ text-decoration: underline;
+}
+
+.dropdown-submenu > a:after {
+ margin-right: -5px;
+}
+
body { padding-top: @navbarHeight + 15 } /* 60px to make the container go all the way to the bottom of the topbar */
footer { margin-top: 25px; padding: 15px 0 16px; border-top: 1px solid #E5E5E5; }
diff --git a/src/site/examples.mkd b/src/site/examples.mkd index 33cb9c4..d8d3dfd 100644 --- a/src/site/examples.mkd +++ b/src/site/examples.mkd @@ -1,6 +1,6 @@ ## Select Statements
-%BEGINCODE%
+---JAVA---
// select * from products
List<Product> allProducts = db.from(p).select();
@@ -23,11 +23,11 @@ List<ProductPrice> productPrices = category = p.category;
price = p.unitPrice;
}});
-%ENDCODE%
+---JAVA---
## Insert Statements
-%BEGINCODE%
+---JAVA---
// single record insertion
db.insert(singleProduct);
@@ -39,11 +39,11 @@ db.insertAll(myProducts); // batch insertion with primary key retrieval
List<Long> myKeys = db.insertAllAndGetKeys(list);
-%ENDCODE%
+---JAVA---
## Update Statements
-%BEGINCODE%
+---JAVA---
// single record update
db.update(singleProduct);
@@ -59,22 +59,21 @@ db.from(p).set(p.productName).to("updated") // reusable, parameterized update query
String q = db.from(p).set(p.productName).toParameter().where(p.productId).is(1).toSQL();
db.executeUpdate(q, "Lettuce");
-
-%ENDCODE%
+---JAVA---
## Merge Statements
Merge statements currently generate the [H2 merge syntax](http://h2database.com/html/grammar.html#merge).
-%BEGINCODE%
+---JAVA---
Product pChang = db.from(p).where(p.productName).is("Chang").selectFirst();
pChang.unitPrice = 19.5;
pChang.unitsInStock = 16;
db.merge(pChang);
-%ENDCODE%
+---JAVA---
## Delete Statements
-%BEGINCODE%
+---JAVA---
// single record deletion
db.delete(singleProduct);
@@ -83,12 +82,11 @@ db.deleteAll(myProducts); // delete query
db.from(p).where(p.productId).atLeast(10).delete();
-
-%ENDCODE%
+---JAVA---
## Inner Join Statements
-%BEGINCODE%
+---JAVA---
final Customer c = new Customer();
final Order o = new Order();
@@ -98,7 +96,6 @@ List<Customer> customersWithLargeOrders = where(o.total).greaterThan(new BigDecimal("500.00")).
groupBy(c.customerId).select();
-
List<CustOrder> orders =
db.from(c).
innerJoin(o).on(c.customerId).is(o.customerId).
@@ -109,11 +106,11 @@ List<CustOrder> orders = orderId = o.orderId;
total = o.total;
}});
-%ENDCODE%
+---JAVA---
## View Statements
-%BEGINCODE%
+---JAVA---
// the view named "ProductView" is created from the "Products" table
@IQView(viewTableName = "Products")
public class ProductView {
@@ -163,14 +160,13 @@ db.from(p).where(p.id).atLeast(250L).and(p.id).lessThan(350L).replaceView(Produc // now drop the view from the database
db.dropView(ProductViewInherited.class);
-
-%ENDCODE%
+---JAVA---
## Dynamic Queries
Dynamic queries skip all field type checking and, depending on which approach you use, may skip model class/table name checking too.
-%BEGINCODE%
+---JAVA---
// where fragment with object parameters
List<Product> restock = db.from(p).where("unitsInStock=? and productName like ? order by productId", 0, "Chef%").select();
@@ -192,5 +188,4 @@ List<Product> restock = db.executeQuery(Product.class, "select * from products w ResultSet rs = db.executeQuery("select * from products");
List<Product> allProducts = db.buildObjects(Product.class, rs);
JdbcUtils.closeSilently(rs, true);
-
-%ENDCODE%
\ No newline at end of file +---JAVA---
diff --git a/src/site/index.mkd b/src/site/index.mkd index 1c2bbd3..7e489b3 100644 --- a/src/site/index.mkd +++ b/src/site/index.mkd @@ -22,11 +22,11 @@ iciql **is not**... </tr>
<tr>
<td>
-%BEGINCODE%
+---JAVA---
Product p = new Product();
List<Product> restock = db.from(p).where(p.unitsInStock).is(0).select();
List<Product> all = db.executeQuery(Product.class, "select * from products");
-%ENDCODE%
+---JAVA---
</td><td>
<br/>
diff --git a/src/site/jaqu_comparison.mkd b/src/site/jaqu_comparison.mkd index 20df5d5..3b060e5 100644 --- a/src/site/jaqu_comparison.mkd +++ b/src/site/jaqu_comparison.mkd @@ -14,6 +14,7 @@ This is an overview of the fundamental differences between the original JaQu pro <tr><td>savepoints</td><td>bulk operations (insert, update, delete) use savepoints with rollback in the event of failure</td><td>--</td></tr>
<tr><th colspan="3">syntax and api</th></tr>
<tr><td>VIEWs</td><td>create readonly views either from a class definition or from a fluent statement</td><td>--</td></tr>
+<tr><td>Foreign Key Constraints</td><td>model classes may be annotated with foreign key constraints</td><td>--</td></tr>
<tr><td>dynamic queries</td><td>methods and where clauses for dynamic queries that build iciql objects</td><td>--</td></tr>
<tr><td>DROP</td><td>syntax to drop a table or view</td><td></td></tr>
<tr><td>BETWEEN</td><td>syntax for specifying a BETWEEN x AND y clause</td><td>--</td></tr>
diff --git a/src/site/model_classes.mkd b/src/site/model_classes.mkd index 8fedf18..ea91bb0 100644 --- a/src/site/model_classes.mkd +++ b/src/site/model_classes.mkd @@ -113,35 +113,35 @@ You may specify default values for an *@IQColumn* by either: 1. specifying the default value string within your annotation<br/>
**NOTE:**<br/>
The annotated default value always takes priority over a field default value.
-%BEGINCODE%
+---JAVA---
// notice the single ticks!
@IQColumn(defaultValue="'2000-01-01 00:00:00'")
Date myDate;
-%ENDCODE%
+---JAVA---
2. setting a default value on the field<br/>
**NOTE:**<br/>
Primitive types have an implicit default value of *0* or *false*.
-%BEGINCODE%
+---JAVA---
@IQColumn
Date myDate = new Date(100, 0, 1);
@IQColumn
int myId;
-%ENDCODE%
+---JAVA---
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.
### Special Case: primitive autoincrement fields and 0
-%BEGINCODE%
+---JAVA---
@IQColumn(autoIncrement = true)
int myId;
-%ENDCODE%
+---JAVA---
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.
### Example Annotated Model
-%BEGINCODE%
+---JAVA---
import com.iciql.Iciql.EnumType;
import com.iciql.Iciql.IQColumn;
import com.iciql.Iciql.IQEnum;
@@ -188,7 +188,59 @@ public class Product { // default constructor
}
}
-%ENDCODE%
+---JAVA---
+
+### Foreign Keys
+
+---JAVA---
+@IQTable(name = "AnnotatedProduct", primaryKey = "id")
+@IQIndexes({ @IQIndex({ "name", "cat" }), @IQIndex(name = "nameidx", type = IndexType.HASH, value = "name") })
+@IQContraintForeignKey(
+ foreignColumns= { "cat" },
+ referenceName = "AnnotatedCategory",
+ referenceColumns = { "categ" },
+ deleteType = ConstraintDeleteType.CASCADE
+)
+public class ProductAnnotationOnlyWithForeignKey {
+
+ public String unmappedField;
+
+ @IQColumn(name = "id", autoIncrement = true)
+ public Long productId;
+
+ @IQColumn(name = "cat", length = 15, trim = true)
+ public String category;
+
+ @IQColumn(name = "name", length = 50)
+ public String productName;
+
+ @SuppressWarnings("unused")
+ @IQColumn
+ private Double unitPrice;
+
+ @IQColumn
+ private Integer unitsInStock;
+}
+---JAVA---
+
+### Views with Field Constraints
+---JAVA---
+@IQView(name = "AnnotatedProductView", tableName = "AnnotatedProduct")
+public class ProductView {
+
+ public String unmappedField;
+
+ @IQColumn(name = "id", autoIncrement = true)
+ @IQConstraint("this <= 7 AND this > 2")
+ public Long productId;
+ @IQColumn(name = "name")
+ public String productName;
+
+ public String toString() {
+ return productName + " (" + productId + ")";
+ }
+}
+---JAVA---
## Interface Configuration
Alternatively, you may map your model classes using the interface approach by implementing the `com.iciql.Iciql` interface.
@@ -231,26 +283,26 @@ You may specify default values for an field by either: 1. specifying the default value string within your *defineIQ()* method<br/>
**NOTE:**<br/>
The defineIQ() value always takes priority over a field default value.
-%BEGINCODE%
+---JAVA---
Date myDate;
public void defineIQ() {
// notice the single ticks!
Define.defaultValue(myDate, "'2000-01-01 00:00:00'");
}
-%ENDCODE%
+---JAVA---
2. setting a default value on the field<br/>
**NOTE:**<br/>
Primitive types have an implicit default value of *0* or *false*.
-%BEGINCODE%
+---JAVA---
Date myDate = new Date(100, 0, 1);
int myId;
-%ENDCODE%
+---JAVA---
### Example Interface Model
-%BEGINCODE%
+---JAVA---
import com.iciql.Iciql;
import com.iciql.Iciql.IQIgnore;
@@ -276,8 +328,7 @@ public class Product implements Iciql { com.iciql.Define.index(productName, category);
}
}
-%ENDCODE%
-
+---JAVA---
## POJO (Plain Old Java Object) Configuration
@@ -309,14 +360,14 @@ You may specify a default value on the field. **NOTE:**<br/>
Primitive types have an implicit default value of *0* or *false*.
-%BEGINCODE%
+---JAVA---
Date myDate = new Date(100, 0, 1);
int myId;
-%ENDCODE%
+---JAVA---
### Example POJO Model
-%BEGINCODE%
+---JAVA---
import com.iciql.Iciql.IQIgnore;
public class Product {
@@ -332,4 +383,4 @@ public class Product { public Product() {
}
}
-%ENDCODE%
\ No newline at end of file +---JAVA---
\ No newline at end of file diff --git a/src/site/tools.mkd b/src/site/tools.mkd index 6d8c348..8c02a12 100644 --- a/src/site/tools.mkd +++ b/src/site/tools.mkd @@ -20,16 +20,16 @@ If you do not have or do not want to annotate your existing model classes, you c 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.
Each `com.iciql.ValidationRemark` returned by the validation has an associated level from the following enum:
-%BEGINCODE%
+---JAVA---
public static enum Level {
CONSIDER, WARN, ERROR;
}
-%ENDCODE%
+---JAVA---
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.
### Sample Model Validation using JUnit 4
-%BEGINCODE%
+---JAVA---
import static org.junit.Assert.assertTrue;
import java.sql.SQLException;
@@ -92,4 +92,4 @@ public class ValidateModels { System.out.println(message);
}
}
-%ENDCODE%
\ No newline at end of file +---JAVA---
\ No newline at end of file diff --git a/src/site/usage.mkd b/src/site/usage.mkd index 7b9d89d..b1f4c48 100644 --- a/src/site/usage.mkd +++ b/src/site/usage.mkd @@ -15,7 +15,7 @@ Use one of the static utility methods to instantiate a Db instance: 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.
-%BEGINCODE%
+---JAVA---
Db db = Db.open("jdbc:h2:mem:", "sa", "sa");
db.insertAll(Product.getList());
db.insertAll(Customer.getList());
@@ -31,7 +31,7 @@ for (Product product : restock) { where(p.productId).is(product.productId).update();
}
db.close();
-%ENDCODE%
+---JAVA---
Please see the [examples](examples.html) page for more code samples.
@@ -41,9 +41,9 @@ Iciql gives you compile-time type-safety, but it becomes inconvenient if your de #### Where String Fragment Approach
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.
-%BEGINCODE%
+---JAVA---
List<Product> restock = db.from(p).where("unitsInStock=? and productName like ? order by productId", 0, "Chef%").select();
-%ENDCODE%
+---JAVA---
#### Db.executeQuery Approaches
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:
@@ -51,24 +51,62 @@ There may be times when the hybrid approach is still too restrictive and you'd p 1. Make sure to _select *_ in your query otherwise db.buildObjects() will throw a RuntimeException
2. There is no model class type checking nor field type checking.
-%BEGINCODE%
+---JAVA---
List<Product> allProducts = db.executeQuery(Product.class, "select * from products");
List<Product> restock = db.executeQuery(Product.class, "select * from products where unitsInStock=?", 0);
// parameterized query which can be cached and re-used later
String q = db.from(p).where(p.unitsInStock).isParameter().toSQL();
List<Product> restock = db.executeQuery(Product.class, q, 0);
-
-%ENDCODE%
+---JAVA---
Or if you want access to the raw *ResultSet* before building your model object instances...
-%BEGINCODE%
+---JAVA---
ResultSet rs = db.executeQuery("select * from products");
List<Product> allProducts = db.buildObjects(Product.class, rs);
// This method ensures the creating statement is closed
JdbcUtils.closeSilently(rs, true);
-%ENDCODE%
+---JAVA---
+
+### Read-only Views
+
+View model classes can inherit their field definitions from a parent table model class.
+
+---JAVA---
+@IQView(name = "AnnotatedProductViewInherited", inheritColumns = true)
+public class ProductViewFromQuery extends ProductAnnotationOnly {
+
+ public String unmappedField;
+
+ @IQColumn(name = "id")
+ public Long productId;
+
+ public String toString() {
+ return productName + " (" + productId + ")";
+ }
+}
+---JAVA---
+
+You can then create or replace the VIEW in the database using a fluent syntax.
+
+---JAVA---
+// create view from query
+ProductAnnotationOnly product = new ProductAnnotationOnly();
+db.from(product).where(product.productId).exceeds(2L)
+ .and(product.productId).atMost(7L).createView(ProductViewFromQuery.class);
+
+// select from the created view
+ProductViewFromQuery view = new ProductViewFromQuery();
+List<ProductViewFromQuery> products = db.from(view).select();
+
+// replace the view
+db.from(product).where(product.productId).exceeds(3L)
+ .and(product.productId).atMost(8L).replaceView(ProductViewFromQuery.class);
+
+// select from the replaced view
+products = db.from(view).select();
+---JAVA---
### Natural Syntax
@@ -80,7 +118,7 @@ This works by decompiling a Java expression, at runtime, to an SQL condition. T A proof-of-concept decompiler is included, but is incomplete.
The proposed syntax is:
-%BEGINCODE%
+---JAVA---
long count = db.from(co).
where(new Filter() { public boolean where() {
return co.id == x
@@ -92,7 +130,7 @@ long count = db.from(co). && co.time.before(java.sql.Time.valueOf("23:23:23"));
}
}).selectCount();
-%ENDCODE%
+---JAVA---
### JDBC Statements, ResultSets, and Exception Handling
@@ -105,23 +143,23 @@ Iciql does not throw any [checked exceptions](http://en.wikipedia.org/wiki/Excep 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.
#### Console Logging
-%BEGINCODE%
+---JAVA---
IciqlLogger.activeConsoleLogger();
IciqlLogger.deactiveConsoleLogger();
-%ENDCODE%
+---JAVA---
#### SLF4J Logging
-%BEGINCODE%
+---JAVA---
Slf4jIciqlListener slf4j = new Slf4jIciqlListener();
slf4j.setLevel(StatementType.CREATE, Level.WARN);
slf4j.setLevel(StatementType.DELETE, Level.WARN);
slf4j.setLevel(StatementType.MERGE, Level.OFF);
IciqlLogger.registerListener(slf4j);
IciqlLogger.unregisterListener(slf4j);
-%ENDCODE%
+---JAVA---
#### Custom Logging
-%BEGINCODE%
+---JAVA---
IciqlListener custom = new IciqlListener() {
public void logIciql(StatementType type, String statement) {
// do log
@@ -129,14 +167,14 @@ IciqlListener custom = new IciqlListener() { };
IciqlLogger.registerListener(custom);
IciqlLogger.unregisterListener(custom);
-%ENDCODE%
+---JAVA---
## Understanding Aliases and Model Classes
Consider the following example:
-%BEGINCODE%
+---JAVA---
Product p = new Product();
List<Product> restock = db.from(p).where(p.unitsInStock).is(0).select();
-%ENDCODE%
+---JAVA---
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.
@@ -152,10 +190,10 @@ The _db.from(p)_ call reinstantiates each member field of p. Those reinstantiat 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.
-%BEGINCODE%
+---JAVA---
final ThreadLocal<Product> p = Utils.newThreadLocal(Product.class);
db.from(p.get()).select();
-%ENDCODE%
+---JAVA---
## Best Practices
@@ -166,7 +204,7 @@ db.from(p.get()).select(); <table class="table">
<tr><th>Not Thread-Safe</th><th>Thread-Safe</th></tr>
<tr><td>
-%BEGINCODE%
+---JAVA---
final Product p = new Product();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Runnable() {
@@ -177,10 +215,10 @@ for (int i = 0; i < 5; i++) { }, "Thread-" + i);
thread.start();
}
-%ENDCODE%
+---JAVA---
</td><td>
-%BEGINCODE%
+---JAVA---
final ThreadLocal<Product> p = Utils.newThreadLocal(Product.class);
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Runnable() {
@@ -191,7 +229,7 @@ for (int i = 0; i < 5; i++) { }, "Thread-" + i);
thread.start();
}
-%ENDCODE%
+---JAVA---
</td></tr>
</table>
\ No newline at end of file |