Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

เปิดโครงการ 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
languagejava
titleImplementation of all basic abstract methods
collapsetrue
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
languagejs
[{
    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
languagexml
<!-- 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
languagejava
    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
languagesql
titleCheck Select Query
select username from dir_user where username = {id}
Code Block
languagesql
titleInsert Query
insert into dir_user
(id, username, firstName, lastName, email, active)
values
({id}, {id}, {firstName}, {lastName}, {email}, 1)

note: {uuid} สามารถใช้สร้าง unique id

Code Block
languagesql
titleUpdate Query
update dir_user set firstName = {firstName}, lastName = {lastName},
email = {email}
where username = {id}

 

Code Block
languagesql
titleDelete Query
delete from TABLE_NAME where id = {id}

ตอนนี้ให้ทดสอบเพื่อเพิ่มผู้ใช้

ตรวจสอบผู้ใช้ที่ถูกสร้างขึ้นในตาราง dir_user

ให้อัปเดตระเบียนเดียวกันโดยส่งรหัสในพารามิเตอร์ URL

ตรวจสอบการปรับปรุงผู้ใช้

มันได้ผล! โปรดอย่าลืมทดสอบคุณสมบัติอื่น ๆ ของปลั๊กอินด้วย (big grin)

8. ขั้นต่อไป แชร์หรือขาย

คุณสามารถดาวน์โหลด source code จาก jdbc_store_binder_src.zip

หากต้องการดาวน์โหลด jar ปลั๊กอินที่พร้อมใช้งานโปรดค้นหา http://marketplace.joget.org/.