...
เราสามารถอ้างถึงการดำเนินการของอื่น ๆ ที่มีอยู่ใน Form Store Binder plugins ได้ แหล่งข้อมูลเริ่มต้นของ Joget สามารถเรียกดูได้ที่ AppUtil.getApplicationContext().getBean("setupDataSource").
6.
...
เตรียม environment ของคุณเพื่อการพัฒนา
เราจำเป็นต้องเตรียมซอร์สโค้ด Joget Workflow ของเราให้พร้อมและสร้างโดยทำตาม this guideline.
...
เปิดโครงการ maven ด้วย IDE ที่คุณโปรดปราน เราแนะนำให้ใช้ NetBeans.
7. เริ่มโค้ด!
a.
...
การขยาย abstract class
...
ของประเภทปลั๊กอิน
สร้างคลาส "JdbcStoreBinder" ภายใต้ "org.joget.tutorial" package. จากนั้นขยายคลาสด้วย org.joget.apps.form.model.FormBinder abstract class.
เพื่อให้มันทำงานเป็น Form Store Binder, เราจะต้องใช้อินเตอร์เฟสของ org.joget.apps.form.model.FormStoreBinder. จากนั้นเราจำเป็นต้องใช้อินเตอร์เฟส org.joget.apps.form.model.FormStoreElementBinder เพื่อให้ปลั๊กอินนี้แสดงเป็นตัวเลือกใน Select Box และเลือกใช้อินเตอร์เฟสของ org.joget.apps.form.model.FormStoreMultiRowElementBinder เพื่อแสดงรายการภายใต้ Select Box ของ grid element.
โปรดอ้างอิงถึง Form Store Binder Plugin.
b.
Implement all the abstractการดำเนินการของ abstract methods ทั้งหมด
เช่นเคย เราต้องใช้ abstract methods ทั้งหมด เราจะใช้ AppPluginUtil.getMessage method เพื่อสนับสนุน i18n และใช้ตัวแปรคงที่ MESSAGE_PATH สำหรับ message resource bundle directory.
| Code Block | ||||||
|---|---|---|---|---|---|---|
| ||||||
package org.joget.tutorial;
import org.joget.apps.app.service.AppPluginUtil;
import org.joget.apps.app.service.AppUtil;
import org.joget.apps.form.model.Element;
import org.joget.apps.form.model.FormBinder;
import org.joget.apps.form.model.FormData;
import org.joget.apps.form.model.FormRowSet;
import org.joget.apps.form.model.FormStoreBinder;
import org.joget.apps.form.model.FormStoreElementBinder;
import org.joget.apps.form.model.FormStoreMultiRowElementBinder;
public class JdbcStoreBinder extends FormBinder implements FormStoreBinder, FormStoreElementBinder, FormStoreMultiRowElementBinder {
private final static String MESSAGE_PATH = "messages/JdbcStoreBinder";
public String getName() {
return "JDBC Store Binder";
}
public String getVersion() {
return "5.0.0";
}
public String getClassName() {
return getClass().getName();
}
public String getLabel() {
//support i18n
return AppPluginUtil.getMessage("org.joget.tutorial.JdbcStoreBinder.pluginLabel", getClassName(), MESSAGE_PATH);
}
public String getDescription() {
//support i18n
return AppPluginUtil.getMessage("org.joget.tutorial.JdbcStoreBinder.pluginDesc", getClassName(), MESSAGE_PATH);
}
public String getPropertyOptions() {
return AppUtil.readPluginResource(getClassName(), "/properties/jdbcStoreBinder.json", null, true, MESSAGE_PATH);
}
public FormRowSet store(Element element, FormRowSet rows, FormData formData) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
} |
จากนั้นเราต้องทำ UI สำหรับผู้ใช้ผู้ดูแลระบบเพื่อให้อินพุตสำหรับปลั๊กอินของเรา ใน getPropertyOptions method, เราได้เตรียม ตัวเลือกคุณสมบัติปลั๊กอิน ไว้ที่ไฟล์ "/properties/jdbcStoreBinder.json". ให้เราสร้าง directory "resources/properties" ภายใต้ "jdbc_store_binder/src/main" directory. หลังจากสร้าง directory, ให้สร้างไฟล์ชื่อ "jdbcStoreBinder.json" ในโฟลเดอร์ "properties"
ในไฟล์ตัวเลือกคุณสมบัติเราจะต้องเลือกตัวเลือกดังนี้ โปรดทราบว่าเราสามารถใช้ "@@message.key@@" syntax เพื่อสนับสนุน i18n ในตัวเลือกคุณสมบัติของเรา
| Code Block | ||
|---|---|---|
| ||
[{
title : '@@form.jdbcStoreBinder.config@@',
properties : [{
name : 'jdbcDatasource',
label : '@@form.jdbcStoreBinder.datasource@@',
type : 'selectbox',
options : [{
value : 'custom',
label : '@@form.jdbcStoreBinder.customDatasource@@'
},{
value : 'default',
label : '@@form.jdbcStoreBinder.defaultDatasource@@'
}],
value : 'default'
},{
name : 'jdbcDriver',
label : '@@form.jdbcStoreBinder.driver@@',
description : '@@form.jdbcStoreBinder.driver.desc@@',
type : 'textfield',
value : 'com.mysql.jdbc.Driver',
control_field: 'jdbcDatasource',
control_value: 'custom',
control_use_regex: 'false',
required : 'true'
},{
name : 'jdbcUrl',
label : '@@form.jdbcStoreBinder.url@@',
type : 'textfield',
value : 'jdbc:mysql://localhost/jwdb?characterEncoding=UTF8',
control_field: 'jdbcDatasource',
control_value: 'custom',
control_use_regex: 'false',
required : 'true'
},{
name : 'jdbcUser',
label : '@@form.jdbcStoreBinder.username@@',
type : 'textfield',
control_field: 'jdbcDatasource',
control_value: 'custom',
control_use_regex: 'false',
value : 'root',
required : 'true'
},{
name : 'jdbcPassword',
label : '@@form.jdbcStoreBinder.password@@',
type : 'password',
control_field: 'jdbcDatasource',
control_value: 'custom',
control_use_regex: 'false',
value : ''
},{
name : 'check_sql',
label : '@@form.jdbcStoreBinder.check_sql@@',
description : '@@form.jdbcStoreBinder.check_sql.desc@@',
type : 'codeeditor',
mode : 'sql',
required : 'true'
},{
name : 'insert_sql',
label : '@@form.jdbcStoreBinder.insert_sql@@',
description : '@@form.jdbcStoreBinder.insert_sql.desc@@',
type : 'codeeditor',
mode : 'sql',
required : 'true'
},{
name : 'update_sql',
label : '@@form.jdbcStoreBinder.update_sql@@',
description : '@@form.jdbcStoreBinder.update_sql.desc@@',
type : 'codeeditor',
mode : 'sql',
required : 'true'
},{
name : 'delete_sql',
label : '@@form.jdbcStoreBinder.delete_sql@@',
description : '@@form.jdbcStoreBinder.delete_sql.desc@@',
type : 'codeeditor',
mode : 'sql',
required : 'true'
}],
buttons : [{
name : 'testConnection',
label : '@@form.jdbcStoreBinder.testConnection@@',
ajax_url : '[CONTEXT_PATH]/web/json/app[APP_PATH]/plugin/org.joget.tutorial.JdbcStoreBinder/service?action=testConnection',
fields : ['jdbcDriver', 'jdbcUrl', 'jdbcUser', 'jdbcPassword'],
control_field: 'jdbcDatasource',
control_value: 'custom',
control_use_regex: 'false'
}]
}] |
เช่นเคย JDBC Options Binder, เราจะต้องเพิ่มปุ่มเพื่อทดสอบการเชื่อมต่อของ JDBC. โปรดอ้างอิงถึง วิธีการพัฒนา JDBC Options Binder บน Web Service Plugin.
| Code Block |
|---|
public FormRowSet store(Element element, FormRowSet rows, FormData formData) {
Form parentForm = FormUtil.findRootForm(element);
String primaryKeyValue = parentForm.getPrimaryKeyValue(formData);
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
DataSource ds = createDataSource();
con = ds.getConnection();
//check for deletion
FormRowSet originalRowSet = formData.getLoadBinderData(element);
if (originalRowSet != null && !originalRowSet.isEmpty()) {
for (FormRow r : originalRowSet) {
if (!rows.contains(r)) {
String query = getPropertyString("delete_sql");
pstmt = con.prepareStatement(getQuery(query));
int i = 1;
for (String obj : getParams(query, r, primaryKeyValue)) {
pstmt.setObject(i, obj);
i++;
}
pstmt.executeUpdate();
}
}
}
if (!(rows == null || rows.isEmpty())) {
//run query for each row
for (FormRow row : rows) {
//check to use insert query or update query
String checkSql = getPropertyString("check_sql");
pstmt = con.prepareStatement(getQuery(checkSql));
int i = 1;
for (String obj : getParams(checkSql, row, primaryKeyValue)) {
pstmt.setObject(i, obj);
i++;
}
String query = getPropertyString("insert_sql");
rs = pstmt.executeQuery();
//record exist, use update query
if (rs.next()) {
query = getPropertyString("update_sql");
}
pstmt = con.prepareStatement(getQuery(query));
i = 1;
for (String obj : getParams(query, row, primaryKeyValue)) {
pstmt.setObject(i, obj);
i++;
}
pstmt.executeUpdate();
}
}
} catch (Exception e) {
LogUtil.error(getClassName(), e, "");
} finally {
try {
if (rs != null) {
rs.close();
}
if (pstmt != null) {
pstmt.close();
}
if (con != null) {
con.close();
}
} catch (Exception e) {
LogUtil.error(getClassName(), e, "");
}
}
return rows;
}
/**
* Used to replaces all syntax like {field_id} to question mark
* @param query
* @return
*/
protected String getQuery(String query) {
return query.replaceAll("\\{[a-zA-Z0-9_]+\\}", "?");
}
/**
* Used to retrieves the value of variables in query
* @param query
* @param row
* @return
*/
protected Collection<String> getParams(String query, FormRow row, String primaryKey) {
Collection<String> params = new ArrayList<String>();
Pattern pattern = Pattern.compile("\\{([a-zA-Z0-9_]+)\\}");
Matcher matcher = pattern.matcher(query);
while (matcher.find()) {
String key = matcher.group(1);
if (FormUtil.PROPERTY_ID.equals(key)) {
String value = row.getId();
if (value == null || value.isEmpty()) {
value = UuidGenerator.getInstance().getUuid();
row.setId(value);
}
params.add(value);
} else if ("uuid".equals(key)) {
params.add(UuidGenerator.getInstance().getUuid());
} else if ("foreignKey".equals(key)) {
params.add(primaryKey);
} else {
String value = row.getProperty(key);
params.add((value != null)?value:"");
}
}
return params;
}
/**
* To creates data source based on setting
* @return
* @throws Exception
*/
protected DataSource createDataSource() throws Exception {
DataSource ds = null;
String datasource = getPropertyString("jdbcDatasource");
if ("default".equals(datasource)) {
// use current datasource
ds = (DataSource)AppUtil.getApplicationContext().getBean("setupDataSource");
} else {
// use custom datasource
Properties dsProps = new Properties();
dsProps.put("driverClassName", getPropertyString("jdbcDriver"));
dsProps.put("url", getPropertyString("jdbcUrl"));
dsProps.put("username", getPropertyString("jdbcUser"));
dsProps.put("password", getPropertyString("jdbcPassword"));
ds = BasicDataSourceFactory.createDataSource(dsProps);
}
return ds;
} |
c. จัดการ dependency libraries ของปลั๊กอินของคุณ
ปลั๊กอินของเราใช้ dbcp, javax.servlet.http.HttpServletRequest และ javax.servlet.http.HttpServletResponse class, ดังนั้นเราต้องเพิ่ม jsp-api และ commons-dbcp library ไปที่ไฟล์ POM ของเรา
| Code Block | ||
|---|---|---|
| ||
<!-- Change plugin specific dependencies here -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.3</version>
</dependency>
<!-- End change plugin specific dependencies here --> |
d. เตรียมปลั๊กอิน internationalization (i18n) ของคุณให้พร้อม
เรากำลังใช้ i18n message key ใน getLabel และ getDescription method. และเรายังใช้ i18n message key ในตัวเลือกคุณสมบัติของเราด้วย. ดังนั้นเราต้องสร้างไฟล์คุณสมบัติ message resource bundle เพื่อปลั๊กอินของเรา
สร้าง directory "resources/messages" ภายใต้ "jdbc_store_binder/src/main" directory. จากนั้นสร้างไฟล์ "JdbcStoreBinder.properties" ในโฟลเดอร์. ในไฟลืคุณสมบัติให้เราเพิ่ม message keys และ label ดังนี้
| Code Block |
|---|
org.joget.tutorial.JdbcStoreBinder.pluginLabel=JDBC Binder
org.joget.tutorial.JdbcStoreBinder.pluginDesc=Used to store form data using JDBC
form.jdbcStoreBinder.config=Configure JDBC Binder
form.jdbcStoreBinder.datasource=Datasource
form.jdbcStoreBinder.customDatasource=Custom Datasource
form.jdbcStoreBinder.defaultDatasource=Default Datasource
form.jdbcStoreBinder.driver=Custom JDBC Driver
form.jdbcStoreBinder.driver.desc=Eg. com.mysql.jdbc.Driver (MySQL), oracle.jdbc.driver.OracleDriver (Oracle), com.microsoft.sqlserver.jdbc.SQLServerDriver (Microsoft SQL Server)
form.jdbcStoreBinder.url=Custom JDBC URL
form.jdbcStoreBinder.username=Custom JDBC Username
form.jdbcStoreBinder.password=Custom JDBC Password
form.jdbcStoreBinder.check_sql=SQL SELECT Query
form.jdbcStoreBinder.check_sql.desc=Used to decide an insert or update operation. Use syntax like {field_id} in query to inject submitted form data.
form.jdbcStoreBinder.insert_sql=SQL INSERT Query
form.jdbcStoreBinder.insert_sql.desc=Use syntax like {field_id} in query to inject submitted form data.
form.jdbcStoreBinder.update_sql=SQL UPDATE Query
form.jdbcStoreBinder.update_sql.desc=Use syntax like {field_id} in query to inject submitted form data.
form.jdbcStoreBinder.delete_sql=SQL DELETE Query
form.jdbcStoreBinder.delete_sql.desc=Used to delete deleted form data in Grid element. Use syntax like {id} in query to inject form data primary key.
form.jdbcStoreBinder.testConnection=Test Connection
form.jdbcStoreBinder.connectionOk=Database connected
form.jdbcStoreBinder.connectionFail=Not able to establish connection. |
e. ลงทะเบียนปลั๊กอินของคุณที่ Felix Framework
เราจะต้องลงทะเบียนคลาสปลั๊กอินของเราในคลาส Activator (Auto generated in the same class package) เพื่อบอก Felix Framework ว่านี่เป็นปลั๊กอิน
| Code Block | ||
|---|---|---|
| ||
public void start(BundleContext context) {
registrationList = new ArrayList<ServiceRegistration>();
//Register plugin here
registrationList.add(context.registerService(JdbcStoreBinder.class.getName(), new JdbcStoreBinder(), null));
} |
f. สร้างและทดสอบ
ให้สร้างปลั๊กอินของเรา เมื่อกระบวนการสร้างเสร็จสิ้นเราจะพบไฟล์ "jdbc_store_binder-5.0.0.jar" ภายใต้ไดเรกทอรี "jdbc_store_binder / target"
จากนั้นให้อัปโหลด jar ปลั๊กอินไปที่ Manage Plugins หลังจากอัปโหลดไฟล์ jar ตรวจสอบอีกครั้งว่าปลั๊กอินถูกอัปโหลดและเปิดใช้งานอย่างถูกต้อง
ให้สร้างแบบฟอร์มเพื่อสร้างและอัปเดตผู้ใช้เป็นตาราง dir_user
จากนั้นกำหนดค่า binder เก็บของฟอร์มด้วย query ต่อไปนี้
| Code Block | ||||
|---|---|---|---|---|
| ||||
select username from dir_user where username = {id} |
| Code Block | ||||
|---|---|---|---|---|
| ||||
insert into dir_user
(id, username, firstName, lastName, email, active)
values
({id}, {id}, {firstName}, {lastName}, {email}, 1) |
note: {uuid} สามารถใช้สร้าง unique id
| Code Block | ||||
|---|---|---|---|---|
| ||||
update dir_user set firstName = {firstName}, lastName = {lastName},
email = {email}
where username = {id} |
| Code Block | ||||
|---|---|---|---|---|
| ||||
delete from TABLE_NAME where id = {id} |
ตอนนี้ให้ทดสอบเพื่อเพิ่มผู้ใช้
ตรวจสอบผู้ใช้ที่ถูกสร้างขึ้นในตาราง dir_user
ให้อัปเดตระเบียนเดียวกันโดยส่งรหัสในพารามิเตอร์ URL
ตรวจสอบการปรับปรุงผู้ใช้
มันได้ผล! โปรดอย่าลืมทดสอบคุณสมบัติอื่น ๆ ของปลั๊กอินด้วย
8. ขั้นต่อไป แชร์หรือขาย
คุณสามารถดาวน์โหลด source code จาก jdbc_store_binder_src.zip
หากต้องการดาวน์โหลด jar ปลั๊กอินที่พร้อมใช้งานโปรดค้นหา http://marketplace.joget.org/.






