การเขียน groovy ให้ติดต่อกับฐานข้อมูล นั้นแท้จริงแล้ว groovySQL ไปใช้ JDBC API ในการเชื่อมต่อ database อีกทีดังนี้
Groovy / Java Application <--> Groovy SQL <--> JDBC API <--> JDBC DriverManager / DataSource <--> Database Server
ข้อมูลที่จำเป็นต้องรู้ก่อนการใช้งาน database
- database URL
- jdbc:hsqldb:hsql://server/dbname ใช้เชื่อมต่อกับ HSQLDB server รองรับการ client หรือ process ที่มากกว่า 1
- jdbc:hsqldb:file:/dir/dbname ใช้เชื่อมต่อกับ single-client โดย HSQLDB จะใช file เป็นตัวเก็บข้อมูล
- jdbc:hsqldb:mem:dbname ใช้เชื่อมต่อกับ database ที่ถูกสร้างและเก็บอยู่ใน memory ซึ่งจะ nonpersistent เหมาะกับการเขียน application เล็กๆ เอาทดลองทำงาน
- username / password ใช้ username: sa (sysadmin) และ password empty (ไม่มี)
- driver class name : org.hsqldb.jdbcDriver
import groovy.sql.Sql
db = Sql.newInstance('jdbc:hsqldb:mem:GinA', 'sa', '', 'org.hsqldb.jdbcDriver');
DriverManager VS DataSource
ถ้าดูตอนการทำงานเชื่อมต่อกับ database จะเห็นว่ามี 2 concept คือ DriverManager และ Data Source ถ้าเราใช้ Sql.newInstance method จะเป็นการใช้ DriverManager แต่ถึงแม้เราจะมี DriverManager แล้ว แต่เพื่อการจัดการประสิทธิภาพที่ดีกว่าเราควรใช้ DataSource เพราะมันมีการจัดการการเชื่อมต่อกับ database ที่ดีกว่าโดยใช้ connect pool (แหล่งเก็บ connection ที่ใช้เชื่อมต่อกับ database) และรองรับ distributed transaction และเรายังสามารถนำ connect นี้กลับมาใช้ใหม่ได้อีก (reuse) เพราะเมื่อแต่ละ transaction ทำงานเสร็จก็จะมีการคืน connect กลับมาที่ pool แต่ถ้าเราลืมคืน connection ตัว Groovy จะเป็นคืน transaction ให้เอง
DataSource จึงกลายมาเป็นส่วนสำคัญเมื่อต้องการจัดการกับทรัพยากรที่มีอยู่อย่างจำกัดให้เกิดประโยชน์สูงสุด เช่นใน application server ก็มีการจัดการทรัพยากรใน application ของตัวเองที่เป็น connection pool
ใน Groovy เราสามารถใช้ datasource ได้ โดยขึ้นอยู่กับ database vendor แต่ละเจ้าซึ่งจะ implement ไว้ที่ javax.sql.DataSource แต่ HSQL มีเตรียมไว้ที่ org.hsqldb.jdbc.jdbcDataSource ตัวอย่างการเชื่อมต่อฐานข้อมูลด้วย DataSource
import groovy.sql.Sql
source = new org.hsqldb.jdbc.jdbcDataSource()
source.database = 'jdbc:hsqldb:mem:GinA'
source.user = 'sa'
source.password = ''
db2 = new Sql(source)
Note: ถ้าเราใช้ application server เราสามารถเอา DataSource มาได้โดยใช้ JNDI ซึ่งข้อดีของการใช้วิธีนี้ คือการจัดการเชื่อมต่อกับฐานข้อมูลนั้นไม่ขึ้นอยู่กับ application ของเรา ใน application ของเราไม่ต้องระบุ database driver หรือ DataSource classs ซึ่งมันช่วยให้เราสามารถ migrate จากฐานข้อมูลหนึ่งไปอีกฐานข้อมูลหนึ่งโดยไม่ส่งผลกระทบถึง application
Note: การให้ได้มาซึ่ง connection ถ้าใช้ Datasource จะใช้วิธีส่ง DataSource เข้าไปใน Sql constructor แต่ถ้าใช้ DriverManager จะส่งค่าต่างๆ เข้าไปทาง Sql.newInstance
ไม่ว่าเราจะใช้ DataSource หรือ DriverManger สุดท้ายก็จะได้มาซึ่ง reference ของ Sql instance
สุดท้ายถ้าเราต้องการ clone Sql instance ขึ้นมาอีกอันสามารถใช้ new Sql(db) ได้ ตอนนี้เรามี sql instance ที่พร้อมจะ connect กับฐานข้อมูลแล้ว และเราต้องการ execute บาง sql statement
Executing SQL
ใช้คำสั่งง่ายๆ db.execute(statement) ในการติดต่อฐานข้อมูลโดยส่ง statement sql เข้าไป
สร้างฐานข้อมูลนักกีฬาจะประกอบด้วย ชื่อ, นามสกุล, วันเกิด และ primary key จากนั้นใส่ index ให้ตารางที่สร้างขึ้น (ฐานข้อมูลส่วนใหญ่รวมทั้ง HSQLDB จะมีการสร้าง index ให้อยู่แล้วโดยอัตโนมัติโดยจะเป็น primary แต่เราสามารถระบุ index ที่ต้องการจะสร้างเพื่อให้ชัดเจนยิ่งขึ้นได้เช่นกัน)
จุดประสงค์ของ application นี้คือทดสอบการส่งคำสั่ง sql จึงไม่ต้องมีการใช้ข้อมูลของเก่าก็ได้ (พยายามยึดหลัก agile ไว้) ดังนั้นจึงลบ table และ index ที่เราสร้างไว้ก่อนแล้วค่อยสร้างใหม่ (Reconstruct) ทุกครั้งที่มีการเรียก application นี้ (agile database programming)
db.execute '''
DROP INDEX athleteIdx IF EXISTS;
DROP TABLE Athlete IF EXISTS;
CREATE TABLE Athlete(
athleteId INTEGER GENERATED BY DEFAULT AS IDENTITY,
firstname VARCHAR(64),
lastname VARCHAR(64),
dateOfBirth DATE
);
CREATE INDEX athleteIdx ON Athlete (athleteId);
'''
เพิ่มข้อมูลลงในตาราง Athelete ด้วยข้อมูลนักวิ่งมาราธอน
db.execute '''
INSERT INTO Athlete (firstname, lastname, dateOfBirth)
VALUES ('Paul', 'Tergat', '1969-06-17');
INSERT INTO Athlete (firstname, lastname, dateOfBirth)
VALUES ('Khalid', 'Khannouchi', '1971-12-22');
INSERT INTO Athlete (firstname, lastname, dateOfBirth)
VALUES ('Ronaldo', 'da Costa', '1970-06-07');
'''
ถ้าใช้วิธีนี้ในการ insert ข้อมูลทั้งโปรแกรมมันยากที่จะอ่านและจัดการข้อมูลทั้งหมด เพราะคำสั่ง sql มันซ้ำกันอยู่เยอะ ดังนั้นเราจะเปลี่ยนใหม่โดยการใช้ prepare statement เข้ามาช่วย prepare statement คือมีรุปแบบการเขียนคล้าย sql statement แต่จะแทนค่าที่รับเข้ามาทางโปรแกรมด้วยเครื่องหมาย question mark แต่สุดท้ายเครื่องหมาย question mark จะถูกแทนที่ด้วยค่าที่รับเข้ามาทางโปรแกรม การใช้วิธีเพิ่ม reuse ได้ ใน Java มี prepare statement โดยใช้ java.sql.PreparedStatement interface
String athleteInsert = '''
INSERT INTO Athlete (firstname, lastname, dateOfBirth)
VALUES (?, ?, ?);
'''
db.execute athleteInsert, ['Paul', 'Tergat', '1969-06-17']
db.execute athleteInsert, ['Khalid', 'Khannouchi', '1971-12-22']
db.execute athleteInsert, ['Ronaldo', 'da Costa', '1970-06-07']
เขียนใหม่ให้อยู่ในรูปแบบ closure โดยส่ง map เข้าไปเป็น argument
def athletes = [
[first: 'Paul', last: 'Tergat', birth: '1969-06-17'],
[first: 'Khalid', last: 'Khannouchi', birth: '1971-12-22'],
[first: 'Ronaldo', last: 'da Costa', birth: '1970-06-07'],
]
athletes.each { athlete ->
db.execute """
INSERT INTO Athlete (firstname, lastname, dateOfBirth)
VALUES (${athlete.first}, ${athlete.last}, ${athlete.birth});
"""
}
Reference : Groovy in Action
つづく
No comments:
Post a Comment