วันนี้เราจะมาว่ากันด้วยเรื่องความสามารถใหม่ในเรื่องของการใช้งาน SimpleJdbc คลาสต่างๆในสปริงสามที่จะช่วยลดปริมาณการเขียนโค้ดลงไปได้เยอะพอสมควรเนื่องจากในสปริงสามนั้น มีการดึงข้อมูลพิเศษของฐานข็อมูลเช่นชื่อคอลัมบ์ชนิดของข้อมูล ออกมากจากคอนเน็คชั่นไดร์เวอร์ที่เราต่อเข้ากับฐานข้อมูลของเราแต่ก่อนเราย้อนกลับไปดูที่ spring2.5 กันก่อนว่าถ้าต้องการ SELECT ขอ้มูลออกมาจากตาราง USERS เพื่อแมปเข้ากับออบเจค User เราจะทำอย่างไร จริงสามารถเขียนได้หลายวิธีแต่ผมเลือกการเขียนแบบแยก Extractor, Mapper ออกจากตัว DAO เพราะไม่ต้องการให้มันรกมากครับ ก่อนอื่นเราต้องไปสร้างคลาสที่ทำหน้าที่จับคู่ระหว่างคอลัมบ์กับแอตทริบิวท์ก่อนโดยมันจะต้องอิมพลีเมนท์อินเทอร์เฟส ResultSetExtractor
public class OwnerExtractor implements ResultSetExtractor {
public Object extractData(ResultSet rs) throws SQLException, DataAccessException {
Owner owner = new Owner();
owner.setId(rs.getInt("ID"));
owner.setFirstName(rs.getString("FIRSTNAME"));
owner.setLastName(rs.getString("LASTNAME"));
owner.setAddress(rs.getString("ADDRESS"));
owner.setCity(rs.getString("CITY"));
owner.setPhone("PHONE");
return owner;
}
}
จากนั้นเราก็ต้องสร้างคลาสที่อิมพลีเมนท์อินเทอร์เฟส RowMapper
public class OwnerMapper implements RowMapper {
public Object mapRow(ResultSet rs, int i) throws SQLException {
OwnerExtractor ownerExtractor = new OwnerExtractor();
return ownerExtractor.extractData(rs);
}
}
สุดท้านเราก็ต้องไปเขียนรายละเอียดการดึงข้อมูลไว้ที่ DAO จริงๆและเรียกใช้คลาสทั้งสองที่เราเพิ่งสร้างขึ้นมาดังนี้
public class OwnerJdbcDAO implements OwnerDAO {
private final Log log = LogFactory.getLog(getClass());
@Autowired
private DataSource dataSource;
public void setDataSource(DataSource ds) {
dataSource = ds;
}
public Owner loadOwner(Integer id) {
log.debug("OwnerJdbcDAO.loadPet");
JdbcTemplate template = new JdbcTemplate(dataSource);
String sql = "SELECT ID, FIRSTNAME, LASTNAME, ADDRESS, CITY, PHONE FROM OWNERS WHERE ID=?";
return (Owner) template.queryForObject(sql, new Object[]{id}, new OwnerMapper());
}
...
จะเห็นว่าท่าเยอะมากกว่าจะได้มาหนึ่งสเตทเมนท์ซึ่งนี่ก็เป็นข้อเสียของการใช้ SpringJDBC เพราะต้องเขียนอะไรต่อมิอะไรเยอะมาก นี่ยังไม่นับรวมคอนฟิกกูเรชั่นต่างๆที่เราต้องไปเขียนเพิ่มอีก ดังนั้นในสปริง3 จึงมีแพกเกจใหม่ชื่อ org.springframework.jdbc.core.simple โดยทีคลาสต่างๆในแพกเกจนี้จะเข้ามาช่วยเราในการทำงานง่ายๆที่ต้องทำบ่อยๆบนฐานข้อมูลเช่นตัวอย่างต่อไปเราจะทำกระบวนการเดียวกันกับตัวอย่างข้างบนแต่เราจะทำผ่านคลาส SimpleJdbcTemplate แทนดังนั้น DAO ของเราจะมีหน้าตาใหม่เป็นแบบนี้ที่หล่อกว่าเกิมสองร้อยเท่า
public class OwnerJdbcDAO implements OwnerDAO {
private final Log log = LogFactory.getLog(getClass());
@Autowired
private DataSource dataSource;
public void setDataSource(DataSource ds) {
dataSource = ds;
}
public Owner loadOwner(Integer id) {
SimpleJdbcTemplate simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
Owner owner;
try {
owner = (Owner)simpleJdbcTemplate.queryForObject("SELECT ID, FIRSTNAME, LASTNAME, ADDRESS, CITY, PHONE FROM owners WHERE ID=?",
new BeanPropertyRowMapper(Owner.class),
id);
}
catch (EmptyResultDataAccessException ex) {
throw new ObjectRetrievalFailureException(Owner.class, new Integer(id));
}
loadPetsAndVisits(owner);
return owner;
}
...
จะเห็นว่าทั้งสองแบบทำงานได้เหมือนกันแต่ที่ต่างออกไปคือเราไม่ต้องไปนั่งแมป เพราะเราใช้ความสามารถของ BeanPropertyRowMapper ในสปริง3 เข้ามาช่วยวันนี้เอาเรื่อง SELECT ไปก่อนพรุ่งนี้เรามาดูความหล่อในเรื่อง
ของการ INSERT, UPDATE กัน

นำ BeanPropertyRowMapper มาใช้ใน JDBCTemplate แล้วช่วยได้เยอะเลย ลดจำนวนการ coding ไปได้เยอะ
แต่เราคงต้องออกแบบ Value/Model class ให้ดีในกรณีที่ระบบเริ่มมีความซับซ้อนขึ้น
ขออภัยด้วยครับ ParameterizedBeanPropertyRowMapper กำลังจะโดนเอาออกจาก Spring3 ดังนั้นให้ไปใช้ BeanPropertyRowMapper แทนครับ
รับแซ่บครับผม
ติดตามอย่างใกล้ชิดครับ
ถ้าเรื่อง ความสะดวก คงใช้ BeanPropertyRowMapper ได้ครับ แต่ ใน API เค้า note ไว้ว่า
“Please note that this class is designed to provide convenience rather than high performance. For best performance consider using a custom RowMapper. ”
ผมว่ามันช้าลงพอสมควรเลยน่ะครับ เพราะว่า BeanPropertyRowMapper ใช้ reflection มาทำงาน
ขอบคุณมากครับ
ขอบคุณสำหรับ บทความดีๆ ครับ
พอดีลองทำแล้วมัน มี Error นะครับ
ผมลองเปลี่ยน Database มาใช้เป็น HR Schema ของ Oracle นะครับ
http://code.google.com/p/spring-learning/source/browse/#svn/branches/error1
ลอง Run Test ดูมันขึ้น Error
java.lang.IllegalArgumentException: Property ‘dataSource’ is required
ขอคำแนะนำหน่อยครับ พอดีได้ไป train ใน Spring3 มาเลยอยากลองทำ Demo ของตัวเองขึ้นมานะครับ
@kimgumshek
แนะนำให้ลอง comment บรรทัดที่มัน load properties file ออกไปก่อน แล้วลองแทนค่าใน dataSource ด้วยค่าจริงทั้งหมดก่อนครับ
bean id=”dataSource”
class=”com.spring.sample.config.DbcpDataSourceFactory”
p:driverClassName=”com.oracle.abc.def.Ghi” p:url=”databaseServer://////”
p:username=”username” p:password=”password”
@kimgumshek
ผมว่าไฟล์ oracle.properties มันหายไปครับ จาก SVN ที่แปะไว้ให้ดูครับ
ลองเอาไปวางไว้ที่ resources แล้วลองใหม่ครับน่าจะรอด
ได้แล้วครับขอบคุณมากครับ