Joget DX 8 Stable Released
The stable release for Joget DX 8 is now available, with a focus on UX and Governance.
ในบทเรียนนี้เราจะทำตาม guideline of developing a plugin เพื่อพัฒนาปลั๊กอิน JDBC Form Store Binder ของเรา โปรดอ้างอิงถึงบทช่วยสอนแรกสุด วิธีการพัฒนา Bean Shell Hash Variable และปลั๊กอินที่เกี่ยวข้องกับ JDBC ต่อไปนี้สำหรับขั้นตอนรายละเอียดเพิ่มเติม
เพื่อจุดประสงค์ในการรวมเราต้องการจัดเก็บข้อมูลฟอร์มของเราลงในตารางฐานข้อมูลอื่นแทนตารางข้อมูล Joget.
Joget Workflow จัดทำประเภทปลั๊กอินที่เรียกว่า Form Store Binder Plugin. เราจะพัฒนาเพื่อรองรับการเชื่อมต่อ JDBC และ query ที่กำหนดเองเพื่อเก็บข้อมูลแบบฟอร์ม
เพื่อการพัฒนาปลั๊กอิน JDBC Store binder, เราจะต้องมีการตั้งค่าการเชื่อมต่อ JDBC และ query แบบกำหนดเองเพื่อเก็บข้อมูลฟอร์มตามข้อมูลฟอร์มที่รวบรวม
เราจะต้องสนับสนุน syntax ในการเอาข้อมูลแบบฟอร์มไปยัง query "{foreignKey}" สามารถใช้สำหรับการจัดเก็บหลายแถว
เราจะต้องสนับสนุน syntax ในการเอาข้อมูลแบบฟอร์มไปยัง UUID value. ในกรณีนี้เราจะใช้ "{uuid}".
ตัวอย่าง: INSERT INTO app_fd_test VALUES ({id}, {name}, {email}, {phone}, {foreignKey});
ข้อมูลที่ส่งทั้งหมดจะเก็บตามคำสั่ง check / insert / update
เราสามารถอ้างถึงการดำเนินการของอื่น ๆ ที่มีอยู่ใน Form Store Binder plugins ได้ แหล่งข้อมูลเริ่มต้นของ Joget สามารถเรียกดูได้ที่ AppUtil.getApplicationContext().getBean("setupDataSource").
เราจำเป็นต้องเตรียมซอร์สโค้ด 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.
สร้างคลาส "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.
เช่นเคย เราต้องใช้ 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; }
ปลั๊กอินของเราใช้ 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 -->
เรากำลังใช้ 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.
เราจะต้องลงทะเบียนคลาสปลั๊กอินของเราในคลาส 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)); }
ให้สร้างปลั๊กอินของเรา เมื่อกระบวนการสร้างเสร็จสิ้นเราจะพบไฟล์ "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
ตรวจสอบการปรับปรุงผู้ใช้
มันได้ผล! โปรดอย่าลืมทดสอบคุณสมบัติอื่น ๆ ของปลั๊กอินด้วย
คุณสามารถดาวน์โหลด source code จาก jdbc_store_binder_src.zip
หากต้องการดาวน์โหลด jar ปลั๊กอินที่พร้อมใช้งานโปรดค้นหา http://marketplace.joget.org/.