ในบทเรียนนี้เราจะทำตาม guideline of developing a plugin เพื่อพัฒนาปลั๊กอิน JDBC Form Store Binder ของเรา โปรดอ้างอิงถึงบทช่วยสอนแรกสุด วิธีการพัฒนา Bean Shell Hash Variable และปลั๊กอินที่เกี่ยวข้องกับ JDBC ต่อไปนี้สำหรับขั้นตอนรายละเอียดเพิ่มเติม
1. ปัญหาคืออะไร?
เพื่อจุดประสงค์ในการรวมเราต้องการจัดเก็บข้อมูลฟอร์มของเราลงในตารางฐานข้อมูลอื่นแทนตารางข้อมูล Joget.
2. ความคิดของคุณในการแก้ปัญหาคืออะไร?
Joget Workflow จัดทำประเภทปลั๊กอินที่เรียกว่า Form Store Binder Plugin. เราจะพัฒนาเพื่อรองรับการเชื่อมต่อ JDBC และ query ที่กำหนดเองเพื่อเก็บข้อมูลแบบฟอร์ม
3. อินพุตที่จำเป็นสำหรับปลั๊กอินของคุณ?
เพื่อการพัฒนาปลั๊กอิน JDBC Store binder, เราจะต้องมีการตั้งค่าการเชื่อมต่อ JDBC และ query แบบกำหนดเองเพื่อเก็บข้อมูลฟอร์มตามข้อมูลฟอร์มที่รวบรวม
- Datasource: การใช้แหล่งข้อมูลที่กำหนดเองหรือแหล่งข้อมูลเริ่มต้นของ Joget
- Custom JDBC Driver: ไดรเวอร์ JDBC สำหรับแหล่งข้อมูลที่กำหนดเอง
- Custom JDBC URL: URL การเชื่อมต่อ JDBC สำหรับแหล่งข้อมูลที่กำหนดเอง
- Custom JDBC Username: ชื่อผู้ใช้สำหรับแหล่งข้อมูลที่กำหนดเอง
- Custom JDBC Password: รหัสผ่านสำหรับแหล่งข้อมูลที่กำหนดเอง
- SQL Check Exist Query: The query เพื่อตรวจสอบว่าแบบสอบถามแทรกหรือปรับปรุงควรจะดำเนินการ
- SQL Insert Query: The query เพื่อแทรกข้อมูลแบบฟอร์ม
- SQL Update Query: The query เพื่อแทรกข้อมูลแบบฟอร์ม
- SQL Delete Query: The query เพื่อลบข้อมูลในแบบฟอร์มที่ถูกลบเมื่อใช้เป็น multirow binder
เราจะต้องสนับสนุน syntax ในการเอาข้อมูลแบบฟอร์มไปยัง query "{foreignKey}" สามารถใช้สำหรับการจัดเก็บหลายแถว
เราจะต้องสนับสนุน syntax ในการเอาข้อมูลแบบฟอร์มไปยัง UUID value. ในกรณีนี้เราจะใช้ "{uuid}".
ตัวอย่าง: INSERT INTO app_fd_test VALUES ({id}, {name}, {email}, {phone}, {foreignKey});
4. ผลลัพธ์และผลลัพธ์ที่คาดหวังจากปลั๊กอินของคุณคืออะไร?
ข้อมูลที่ส่งทั้งหมดจะเก็บตามคำสั่ง check / insert / update
5. มีทรัพยากร / API ใด ๆ ที่สามารถนำมาใช้ซ้ำได้?
เราสามารถอ้างถึงการดำเนินการของอื่น ๆ ที่มีอยู่ใน Form Store Binder plugins ได้ แหล่งข้อมูลเริ่มต้นของ Joget สามารถเรียกดูได้ที่ AppUtil.getApplicationContext().getBean("setupDataSource").
6. เตรียมสภาพแวดล้อมการพัฒนาของคุณ
เราจำเป็นต้องเตรียมซอร์สโค้ด Joget Workflow ของเราให้พร้อมและสร้างโดยทำตาม this guideline.
บทเรียนนี้จัดทำขึ้นด้วย Macbook Pro และ Joget Source Code version 5.0.0. โปรดอ้างอิงถึง แนวทางสำหรับการพัฒนาปลั๊กอิน สำหรับคำสั่งแพลตฟอร์มอื่น ๆ
ให้กล่าวว่าไดเรกทอรีโฟลเดอร์ของเราดังต่อไปนี้
- Home
- joget
- plugins
- jw-community
-5.0.0
ไดเรกทอรี "ปลั๊กอิน" คือโฟลเดอร์ที่เราจะสร้างและจัดเก็บปลั๊กอินทั้งหมดของเราและไดเรกทอรี "jw-community" เป็นที่เก็บซอร์สโค้ด Joget Workflow
เรียกใช้คำสั่งต่อไปนี้เพื่อสร้างโครงการ maven ในไดเรกทอรี "ปลั๊กอิน"
cd joget/plugins/ ~/joget/jw-community/5.0.0/wflow-plugin-archetype/create-plugin.sh org.joget.tutorial jdbc_store_binder 5.0.0
จากนั้น the shell script จะขอให้เราใส่ปลั๊กอินสำหรับรุ่นของคุณและขอให้เรายืนยันก่อนที่จะสร้างโครงการ Maven
Define value for property 'version': 1.0-SNAPSHOT: : 5.0.0 [INFO] Using property: package = org.joget.tutorial Confirm properties configuration: groupId: org.joget.tutorial artifactId: jdbc_store_binder version: 5.0.0 package: org.joget.tutorial Y: : y
เราควรได้รับข้อความ "BUILD SUCCESS" ที่ปรากฏในเครื่องของเราและโฟลเดอร์ "jdbc_store_binder" ที่สร้างในโฟลเดอร์ "ปลั๊กอิน"
เปิดโครงการ maven ด้วย IDE ที่คุณโปรดปราน เราแนะนำให้ใช้ NetBeans.
7. เริ่มโค้ด!
a. Extending the abstract class of a plugin type
สร้างคลาส "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 methods
เช่นเคย เราต้องใช้ abstract methods ทั้งหมด เราจะใช้ AppPluginUtil.getMessage method เพื่อสนับสนุน i18n และใช้ตัวแปรคงที่ MESSAGE_PATH สำหรับ message resource bundle directory.
จากนั้นเราต้องทำ UI สำหรับผู้ใช้ผู้ดูแลระบบเพื่อให้อินพุตสำหรับปลั๊กอินของเรา ใน getPropertyOptions method, เราได้เตรียม ตัวเลือกคุณสมบัติปลั๊กอิน ไว้ที่ไฟล์ "/properties/jdbcStoreBinder.json". ให้เราสร้าง directory "resources/properties" ภายใต้ "jdbc_store_binder/src/main" directory. หลังจากสร้าง directory, ให้สร้างไฟล์ชื่อ "jdbcStoreBinder.json" ในโฟลเดอร์ "properties"
ในไฟล์ตัวเลือกคุณสมบัติเราจะต้องเลือกตัวเลือกดังนี้ โปรดทราบว่าเราสามารถใช้ "@@message.key@@" syntax เพื่อสนับสนุน i18n ในตัวเลือกคุณสมบัติของเรา
[{
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.
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 ของเรา
<!-- 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 ดังนี้
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 ว่านี่เป็นปลั๊กอิน
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 ต่อไปนี้
select username from dir_user where username = {id}
insert into dir_user
(id, username, firstName, lastName, email, active)
values
({id}, {id}, {firstName}, {lastName}, {email}, 1)
note: {uuid} สามารถใช้สร้าง unique id
update dir_user set firstName = {firstName}, lastName = {lastName},
email = {email}
where username = {id}
delete from TABLE_NAME where id = {id}
ตอนนี้ให้ทดสอบเพื่อเพิ่มผู้ใช้
ตรวจสอบผู้ใช้ที่ถูกสร้างขึ้นในตาราง dir_user
ให้อัปเดตระเบียนเดียวกันโดยส่งรหัสในพารามิเตอร์ URL
ตรวจสอบการปรับปรุงผู้ใช้
มันได้ผล! โปรดอย่าลืมทดสอบคุณสมบัติอื่น ๆ ของปลั๊กอินด้วย
8. ขั้นต่อไป แชร์หรือขาย
คุณสามารถดาวน์โหลด source code จาก jdbc_store_binder_src.zip
หากต้องการดาวน์โหลด jar ปลั๊กอินที่พร้อมใช้งานโปรดค้นหา http://marketplace.joget.org/.






