星期六, 5月 28, 2011

jiql JDBC

Overview
jiql is a JDBC wrapper for accessing Google DataStore on Google App Engine for JAVA.
jiql supports the use of standard SQL as a method for accessing
the DataStore 
 
Architecture
jiql introduces a concept called "Table Leafs". Table Leafs
are DataStore Kinds that can be dynamically added or 
chained together in infinite amounts to represent a Table. 
 
Table Leafs provides a standard method, with SQL, to
retrieve entities of more than the "1000 limit" at a time.
 
For optimization, entities in the same Table Leaf, has
the same entity group parent. That means all of the
members in the Table Leaf, are located in the same
network node of the Google DataStore Cloud, for faster
access.
 
To disable Table Leaves, add the following at the end of the
CREATE TABLE statement:
tableleafs=false
 
 
Quickstart
Download jiql.jar java database engine and driver library and place under WEB-INF/lib/
NOTE: The appengine-api.jar must also be placed under WEB-INF/lib. This file comes
with the GAE SDK. Follow the steps below to create a JDBC connection with jiql on
GAE-J in your JAVA code:
 
String url = "jdbc:jiql://local";
String password = "";
String user = "";
 
Properties props = new Properties();
props.put("user",user);
props.put("password",password);
  
Class clazz = Class.forName("org.jiql.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
Connection Conn = driver.connect(url,props);
Below is the link to a GAE-Ready JDBC guestbook application:
 
After downloading jiql-jdbc-guestbook.war, unpack to your GAE Application directory.
Edit the ./WEB-INF/appengine-web.xml file and add your GAE Application ID.
Upload to Google App Engine for JAVA.
 
You must first run the following JSP, which will create the Database table.
http://yourapp.appspot.com/init.jsp
 
to start using the JDBC guestbook application.
 
The source code may also be downloaded at the following URL:
 
NOTE: Sample applications may not contain the latest jiql.jar. To ensure you have the
latest download and add to application. Download the latest jiql.jar from the following URL:
 
 
Remote Access
You can access Google DataStore from a remote client via the JiqlServlet.
This requires setting up the GAE-J Server as well as the Client Application configuration.
GAE-J Server Configuration
First configure the JiqlServlet in the web.xml like so:
    <servlet>
        <servlet-name>jiqlservlet</servlet-name>
        <servlet-class>org.jiql.JiqlServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>jiqlservlet</servlet-name>
        <url-pattern>/jiqlservlet</url-pattern>
    </servlet-mapping>
 
Next add authenication information in the jiql.properties file:
File: ./WEB-INF/jiql.properties
Entries:
user=admin
password=jiql
 
Client Application Configuration
Follow the steps below to create a JDBC connection with jiql on the Client in your JAVA code:
String url = "jdbc:jiql:https://yourapp.appspot.com/jiqlservlet";
String user = "admin";
String password = "jiql";
//or
//String url = "jdbc:jiql:https://yourapp.appspot.com/jiqlservlet?user=admin&password=jiql";
 
Properties props = new Properties();
props.put("user",user);
props.put("password",password);
  
Class clazz = Class.forName("org.jiql.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
Connection Conn = driver.connect(url,props);
 Required Libraries for Remote Client
jiql.jar
appengine-api.jar
Apache Commons HTTPClient - you may download the bundled JAR here.
 
Required Libraries for the Server
Apache Commons FileUpload available at the following URL:
 
 
 
Transactions
jiql has limited support for Transactions.
Transactions support is based on the Google DataStore Transaction
engine. Google DataStore does not support Global Transactions.
A Transaction is limited to Entities/Rows within a Group or Table/Kind.
This mean you are not able to perform operations on multiple
Tables/Kinds within the same Transaction. To enable Transactions,
set the enable.transactions Connection property option to true.
NOTE: Transaction are NOT supported for Remote connections.
 
 
 
Limitations
FILTERS
For large data sets, the left most filter, should not return
a result set of more than about a couple thousand items.
A timeout may result. In this case you should try a narrower
filter. If the query is comprised of multiple filters, you may
place the filter that returns the least items, to the left of all
the other filters.
 
JOINS
When working with large data sets, JOINS should be combined with
at least 1 filter acting on a specific table. Otherwise, the query would
involve traversing the entirety of all the tables in the JOIN. Such
an operation is time consuming and may create a Google App Engine Timeout
if the operation exceeds 30 seconds.
 
 
Optional Properties
date.format
Specifies the Date Format of a DATETIME field.
e.g. date.format=yyyy/MM/dd
 
enable.transactions
Enable Transactions (please refer to the Transactions topic)
e.g. enable.transactions=true
 
DatabaseProductVersion
Used to emulate other Databases, such as MySQL.
e.g. DatabaseProductVersion=4.0.0 
 
DriverVersion
Used to emulate other Databases, such as MySQL.
e.g. DriverVersion=mysql-connector-java-5.0.4 ()
 
Catalog
Used to specify a Database Catalog.
e.g. Catalog=jiql 
 
MetaCache
Used for Caching MetaData, such as DatabaseMetaData.
e.g. MetaCache=true
 
 
 
jiql Functions
In addition to standard SQL functions, there are a number of jiql specific functions.
LOAD
LOAD DATA INTO TABLE table_name INTEXT column1,column2
1,'text1'
2,'text2'
 
tableleafs
specify whether to use Table Leafs or not (default true).
tableleafs=false
 
prefix
specify whether to add a prefix to Tables created by jiql (default true)
prefix=false
 
prefix_value
specify whether to add a customized prefix name to Tables created by jiql (default jiql)
prefix_value=jiql
 
 
PHP
How to run PHP with your same MySQL queries
Running PHP on Google App Engine for JAVA requires jiql with a modified version of Quercus,
a Java implementation of PHP 5. The following URL has the downloads:
jiql-quercus.war - The modified Quercus Application bundled with jiql.
jiql-quercus.src.zip - The modified Quercus source files.
 
After downloading jiql-quercus.war, unpack to your GAE Application directory.
Add your PHP files to the root of the unpacked Quercus application.
Edit the ./WEB-INF/appengine-web.xml file and add your GAE Application ID.
Upload to Google App Engine for JAVA.
 
Below is a simple PHP application from PLUS2NET as a starting PHP example:
NOTE: Sample applications may not contain the latest jiql.jar. To ensure you have the
latest download and add to application. Download the latest jiql.jar from the following URL:
 
 
JRuby on RAILS
Download jiql.jar java database engine and driver library and place under WEB-INF/lib/.
Configure your database.yml like so:
production:
  adapter: jdbc
  driver: org.jiql.jdbc.Driver
  url: jdbc:jiql:local
  username: admin
  password: appcloem
  encoding: utf8
Now of course Google App Engine for JAVA does not allow you to perform pre-deployment tasks,
such as running rake to setup your RAILS application database. You have 2 choices.
First configure your jiql for remote access. Then run the rake DB task from your PC.
For this copy of your Application, you would configure the database.yml like so:
production:
  adapter: jdbc
  driver: org.jiql.jdbc.Driver
  url: jdbc:jiql:https://myapp.appspot.com/jiqlservlet?user=admin&password=jiql
  username: admin
  password: appcloem
  encoding: utf8
The second choice is to export the Database to a SQL formatted script.
With the SQL script, you have a number of choices to import the SQL script to your
jiql Database, which includes the use of Eclipse Database Tools Project (DTP) or jiqlAdmin.
 
Below is the link to a GAE-Ready phonebook application WAR:
 
After downloading jiql-ruby-rails-phonebook.war, unpack to your GAE Application directory.
Edit the ./WEB-INF/appengine-web.xml file and add your GAE Application ID.
Upload to Google App Engine for JAVA.
 
Since we are not able to run 'rake db:migrate' on Google App Engine,
you must first run the following JSP, which will create the Database table.
http://yourapp.appspot.com/init.jsp
 
to start using the RAILS phonebook application.
 
NOTE: Sample applications may not contain the latest jiql.jar. To ensure you have the
latest download and add to application. Download the latest jiql.jar from the following URL:
 
 
jiql Administration
As jiql uses the standard JDBC protocol to manage Google DataStore, there are a number
of tools that can be used locally or remotely to manage jiql. Available database management
tools include:
jiqlAdmin Data Querying tool for Google's DataStore.
Eclipse Database Tools Project (DTP). When configuring DTP for jiql,
the Eclipse Connection Type should be set to Generic JDBC_1.
 
 
 
Source - Compiling
 
- Download the source here
 
- JAVA JDK 1.6 or higher is required. The reason for this is that jiql implements
the latest JDBC API. The latest JDBC references SQL extensions that
are included with JDK 1.6. These SQL extensions are not included in
previous JAVA JDK releases. 
 
- There are 2 ways of compiling the source. But first an explanation
of the JAVA source tree.
 
org.jiql - is the core jiql source code.
tools. - are code for a general library used by jiql and other projects.
 
- Minimum dependencies for compiling:
appengine-api.jar
hibernate3.jar
 
For minimum compiling:
Download the jiql.jar and uncompress to your output folder.
Add the output path to your classpath.
This will allow you to edit org.jiql.* code and rebuild.
 
- However to compile all code, including tools.*, the following dependencies must
be added to your classpath:
servlet-api.jar
ant.jar
 
NOTE: jiql depends on a number of properties files located in the jiql package
tree. These properties files are not included in the source.zip archive. The
properties files are included in the SVN source. They may also be extracted

星期三, 5月 25, 2011

JSR 303 - Bean Validation 介紹及最佳實踐

http://www.ibm.com/developerworks/cn/java/j-lo-jsr303/index.html?ca=drs-

JSR 303 - Bean Validation 介紹及最佳實踐

安 大鵬, 軟件工程師, IBM
楊 樂, 軟件工程師, IBM
翁 志弘, 軟件工程師, IBM

簡介: JSR 303 – Bean Validation 是一個數據驗證的規範,2009 年 11 月確定最終方案。2009 年 12 月 Java EE 6 發佈,Bean Validation 作為一個重要特性被包含其中。本文將對 Bean Validation 的主要功能進行介紹,並通過一些示例來演示如何在 Java 開發過程正確的使用 Bean Validation。

發佈日期: 2011 年 5 月 24 日 
級別: 中級 
訪問情況 93 次瀏覽 
建議: 0 (添加評論)

1 star2 stars3 stars4 stars5 stars 平均分 (共 0 个评分 )

關於 Bean Validation

在任何時候,當你要處理一個應用程序的業務邏輯,數據校驗是你必須要考慮和面對的事情。應用程序必須通過某種手段來確保輸入進來的數據從語義上來講是正確的。在通常的情況下,應用程序是分層的,不同的層由不同的開發人員來完成。很多時候同樣的數據驗證邏輯會出現在不同的層,這樣就會導致代碼冗餘和一些管理的問題,比如說語義的一致性等。為了避免這樣的情況發生,最好是將驗證邏輯與相應的域模型進行綁定。

Bean Validation 為 JavaBean 驗證定義了相應的元數據模型和 API。缺省的元數據是 Java Annotations,通過使用 XML 可以對原有的元數據信息進行覆蓋和擴展。在應用程序中,通過使用 Bean Validation 或是你自己定義的 constraint,例如@NotNull@Max@ZipCode, 就可以確保數據模型(JavaBean)的正確性。constraint 可以附加到字段,getter 方法,類或者接口上面。對於一些特定的需求,用戶可以很容易的開發定製化的 constraint。Bean Validation 是一個運行時的數據驗證框架,在驗證之後驗證的錯誤信息會被馬上返回。

下載 JSR 303 – Bean Validation 規範 http://jcp.org/en/jsr/detail?id=303

Hibernate Validator 是 Bean Validation 的參考實現 . Hibernate Validator 提供了 JSR 303 規範中所有內置 constraint 的實現,除此之外還有一些附加的 constraint。如果想瞭解更多有關 Hibernate Validator 的信息,請查看http://www.hibernate.org/subprojects/validator.html

Bean Validation 中的 constraint


表 1. Bean Validation 中內置的 constraint

Constraint詳細信息
@Null被註釋的元素必須為 null
@NotNull被註釋的元素必須不為 null
@AssertTrue被註釋的元素必須為 true
@AssertFalse被註釋的元素必須為 false
@Min(value)被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@Max(value)被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@DecimalMin(value)被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@DecimalMax(value)被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@Size(max, min)被註釋的元素的大小必須在指定的範圍內
@Digits (integer, fraction)被註釋的元素必須是一個數字,其值必須在可接受的範圍內
@Past被註釋的元素必須是一個過去的日期
@Future被註釋的元素必須是一個將來的日期
@Pattern(value)被註釋的元素必須符合指定的正則表達式

Constraint詳細信息
@Email被註釋的元素必須是電子郵箱地址
@Length被註釋的字符串的大小必須在指定的範圍內
@NotEmpty被註釋的字符串的必須非空
@Range被註釋的元素必須在合適的範圍內


一個 constraint 通常由 annotation 和相應的 constraint validator 組成,它們是一對多的關係。也就是說可以有多個 constraint validator 對應一個 annotation。在運行時,Bean Validation 框架本身會根據被註釋元素的類型來選擇合適的 constraint validator 對數據進行驗證。

有些時候,在用戶的應用中需要一些更複雜的 constraint。Bean Validation 提供擴展 constraint 的機制。可以通過兩種方法去實現,一種是組合現有的 constraint 來生成一個更複雜的 constraint,另外一種是開發一個全新的 constraint。

創建一個包含驗證邏輯的簡單應用(基於 JSP)

在本文中,通過創建一個虛構的訂單管理系統(基於 JSP 的 web 應用)來演示如何在 Java 開發過程中應用 Bean Validation。該簡化的系統可以讓用戶創建和檢索訂單。

系統設計和運用的技術


圖 1. 系統架構
圖 1. 系統架構 

圖 1 是報表管理系統的結構圖,是典型的 MVC(Model-View-Controller)應用。Controller 負責接收和處理請求,Servlet 扮演 Controller 的角色去處理請求、業務邏輯並轉向合適的 JSP 頁面。在 Servlet 中對數據進行驗證。JSP 扮演 View 的角色以圖型化界面的方式呈現 Model 中的數據方便用戶交互。Model 就是此系統進行操作的數據模型,我們對這部分加以簡化不對數據進行持久化。

數據模型


圖 2. 數據模型
圖 2. 數據模型 

圖 2 展示的是訂單管理系統的數據模型。

聲明了 contraint 的 JavaBean


清單 1. Order.java

				 
public class Order {
// 必須不為 null, 大小是 10
@NotNull
@Size(min = 10, max = 10)
private String orderId;
// 必須不為空
@NotEmpty
private String customer;
// 必須是一個電子信箱地址
@Email
private String email;
// 必須不為空
@NotEmpty
private String address;
// 必須不為 null, 必須是下面四個字符串'created', 'paid', 'shipped', 'closed'其中之一
// @Status 是一個定製化的 contraint
@NotNull
@Status
private String status;
// 必須不為 null
@NotNull
private Date createDate;
// 嵌套驗證
@Valid
private Product product;


getter 和 setter
}

				 
public class Product {
// 必須非空
@NotEmpty
private String productName;
// 必須在 8000 至 10000 的範圍內
// @Price 是一個定製化的 constraint
@Price
private float price;

Getter 和 setter
}

				 
// 'to'所表示的日期必須在'from'所表示的日期之後
// @QueryConstraint 是一個定製化的 constraint
@QueryConstraint
public class OrderQuery {
private Date from;
private Date to;
… omitted …
Getter and setter
}


定製化的 constraint

@Price是一個定製化的 constraint,由兩個內置的 constraint 組合而成。


清單 4. @Price 的 annotation 部分

				 
// @Max 和 @Min 都是內置的 constraint
@Max(10000)
@Min(8000)
@Constraint(validatedBy = {})
@Documented
@Target( { ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Price {
String message() default "錯誤的價格";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}


@Status是一個新開發的 constraint.


清單 5. @Status 的 annotation 部分

				 
@Constraint(validatedBy = {StatusValidator.class})
@Documented
@Target( { ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Status {
String message() default "不正確的狀態 , 應該是 'created', 'paid', shipped', closed'其中之一";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

				 
public class StatusValidator implements ConstraintValidator<Status, String>{
private final String[] ALL_STATUS = {"created", "paid", "shipped", "closed"};
public void initialize(Status status) {
}
public boolean isValid(String value, ConstraintValidatorContext context) {
if(Arrays.asList(ALL_STATUS).contains(value))
return true;
return false;
}
}


Bean Validation API 使用示例

創建訂單

用戶在創建一條訂單記錄時,需要填寫以下信息:訂單編號,客戶,電子信箱,地址,狀態,產品名稱,產品價格


圖 3. 創建訂單
圖 3. 創建訂單 

對這些信息的校驗,使用 Bean Validation API


清單 7. 代碼片段

				 
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
HttpSession session = req.getSession();
// 從 request 中獲取輸入信息
String orderId = (String) req.getParameter("orderId");
String customer = (String) req.getParameter("customer");
String email = (String) req.getParameter("email");
String address = (String) req.getParameter("address");
String status = (String) req.getParameter("status");
String productName = (String) req.getParameter("productName");
String productPrice = (String) req.getParameter("productPrice");
// 將 Bean 放入 session 中
Order order = new Order();
order.setOrderId(orderId);
order.setCustomer(customer);
order.setEmail(email);
order.setAddress(address);
order.setStatus(status);
order.setCreateDate(new Date());
Product product = new Product();
product.setName(productName);
if(productPrice != null && productPrice.length() > 0)
product.setPrice(Float.valueOf(productPrice));
order.setProduct(product);
session.setAttribute("order", order);
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Order>> violations = validator.validate(order);
if(violations.size() == 0) {
session.setAttribute("order", null);
session.setAttribute("errorMsg", null);
resp.sendRedirect("creatSuccessful.jsp");
} else {
StringBuffer buf = new StringBuffer();
ResourceBundle bundle = ResourceBundle.getBundle("messages");
for(ConstraintViolation<Order> violation: violations) {
buf.append("-" + bundle.getString(violation.getPropertyPath().toString()));
buf.append(violation.getMessage() + "<BR>\n");
}
session.setAttribute("errorMsg", buf.toString());
resp.sendRedirect("createOrder.jsp");
}
}


如果用戶不填寫任何信息提交訂單,相應的錯誤信息將會顯示在頁面上


圖 4. 驗證後返回錯誤信息
圖 4. 驗證後返回錯誤信息 

其實在整個程序的任何地方都可以調用 JSR 303 API 去對數據進行校驗,然後將校驗後的結果返回。


清單 8. 調用 JSR 303 API 進行校驗

				 
Order order = new Order();

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Order>> violations = validator.validate(order);


結束語

JSR 303 的發布使得在數據自動綁定和驗證變得簡單,使開發人員在定義數據模型時不必考慮實現框架的限制。當然 Bean Validation 還只是提供了一些最基本的 constraint,在實際的開發過程中,用戶可以根據自己的需要組合或開發出更加複雜的 constraint


參考資料

學習

討論

  • 加入 developerWorks 中文社區。查看開發人員推動的博客、論壇、組和維基,並與其他 developerWorks 用戶交流。

作者簡介

安大鵬,在 IBM Global Business Solution Center 工作,專注於開發可重用的軟件資產。

楊樂是IBM中國軟件開發中心 GBSC 的軟件工程師,其所在團隊從事 SOA 行業方案的設計和開發,他的專長和興趣包括 J2EE、Web 服務、XML 和 Security 等。您可以通過 yangle@cn.ibm.com和他聯繫。

翁志弘在 IBM Global Business Solution Center 工作,專注於開發可重用的軟件 assets。