SimpleJdbcTemplate ในสปริง3

November 3rd, 2009 by roofimon Leave a reply »

วันนี้เราจะมาว่ากันด้วยเรื่องความสามารถใหม่ในเรื่องของการใช้งาน 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 กัน

Advertisement

11 comments

  1. up1 says:

    นำ BeanPropertyRowMapper มาใช้ใน JDBCTemplate แล้วช่วยได้เยอะเลย ลดจำนวนการ coding ไปได้เยอะ

    แต่เราคงต้องออกแบบ Value/Model class ให้ดีในกรณีที่ระบบเริ่มมีความซับซ้อนขึ้น

  2. roofimon says:

    ขออภัยด้วยครับ ParameterizedBeanPropertyRowMapper กำลังจะโดนเอาออกจาก Spring3 ดังนั้นให้ไปใช้ BeanPropertyRowMapper แทนครับ

  3. up1 says:

    รับแซ่บครับผม

  4. soda.spring says:

    ติดตามอย่างใกล้ชิดครับ

  5. javafanclub says:

    ถ้าเรื่อง ความสะดวก คงใช้ 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 มาทำงาน

  6. roofimon says:

    ขอบคุณมากครับ

  7. javafanclub says:

    ขอบคุณสำหรับ บทความดีๆ ครับ

  8. kimgumshek says:

    พอดีลองทำแล้วมัน มี 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 ของตัวเองขึ้นมานะครับ

  9. roofimon says:

    @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”

  10. roofimon says:

    @kimgumshek

    ผมว่าไฟล์ oracle.properties มันหายไปครับ จาก SVN ที่แปะไว้ให้ดูครับ
    ลองเอาไปวางไว้ที่ resources แล้วลองใหม่ครับน่าจะรอด

  11. kimgumshek says:

    ได้แล้วครับขอบคุณมากครับ

Leave a Reply

You must be logged in to post a comment.