跳至主要内容

Legacy DaoSupport And Template API

Legacy DaoSupport And Template API

In the Spring core framework(provided in the spring-jdbc and spring-orm maven dependency), it has provided a series of standard DaoSupport APIs and Template pattern to help user to retrieve data from database and save data back into database.

Jdbc support

There is a JdbcDaoSupport class and JdbcTempalte class provided for Jdbc operations.
Using these APIs in your project is very simple and stupid.
  1. Firstly register a DataSource bean, and also declare a Jdbc transaction manager(we will discuss Spring transaction in future posts).
    <jdbc:embedded-database id="dataSource" type="H2">
        <jdbc:script 
        location="classpath:/com/hantsylabs/example/spring/config/schema.sql" /> 
    </jdbc:embedded-database>
    <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    
    In this codes fragment, we are using a H2 embedded database, and execute a predefined sql scripts to create the tables and insert the initial test data when the application starts up.
  2. Declare your Dao interface and provide your implementation to subclass JdbcDaoSupprt.
    public interface ConferenceDao {
        public abstract Conference findById(Long id);
        public abstract Long save(Conference conference);
        public abstract void delete(Long id);
        public abstract void delete(Conference obj);
        public abstract void update(Conference conference);
        public abstract void deleteAll();
        public abstract Conference findBySlug(String string);
    }
    
    In your implementation class, inject the DataSouce bean on the class constructor. If you explore the JdbcDaoSupport class in your IDE, you will find it had already provided a JdbcTemplate bean for you, you can use getJdbcTemplate() method to access it.
    JdbcTemplate provides a lots of methods to simplify Jdbc operations.
    public class JdbcConferenceDaoImpl 
    extends JdbcDaoSupport 
    implements ConferenceDao {
    
        @Autowired
        public JdbcConferenceDaoImpl(DataSource dataSource) {
            super();
            setDataSource(dataSource);
        }
    
        @Override
        public Conference findById(Long id) {
            return jdbcTemplate.queryForObject(
                    "select conference where id =?", new Object[] { id },
                    new ConferenceMapper());
        }
    }
    
    Alternatively, you can declare a JdbcTemplate bean(and inject the DataSource bean) yourself, and use JdbcTemplate in your implementation class directly. The benefit is you are free from subclassing the JdbcDaoSupport class.
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
    </bean> 
    
    Now you can use the injected JdbcTemplate directly.
    public class JdbcConferenceDaoImpl 
    implements ConferenceDao {
    
    @Autowired
    JdbcTemplate jdbcTemplate;
    
    @Override
    public Conference findById(Long id) {
        return jdbcTemplate.queryForObject(
            "select conference where id =?", new Object[] { id },
            new ConferenceMapper());
    }
    }
    
  3. A glance at the JdbcTemplate API.
    JdbcTemaplate provides a series of methods for sql execution, the generic method is execute, there are several variants provided for accepting different parameters. Some specific-purpose methods are also provided, such as:
    query is designated for executing a general select * from table statement.
    public Conference findBySlug(String slug) {
    List<Conference> confs = getJdbcTemplate().query(
                "select * from conference where slug=?", new Object[] { slug },
                new ConferenceMapper());
    if (!confs.isEmpty()) {
        return confs.get(0);
    }
    return null;
    }
    
    Generally, you can implement a RowMapper, RowCallbackHandler or ResultSetExtacter to extract the data from Jdbc ResultSet and wrap them into your own object.
    private class ConferenceMapper 
    implements ParameterizedRowMapper<Conference> {
    
    @Override
    public Conference mapRow(ResultSet rs, int rowNum) throws SQLException {
        Conference conference = new Conference();
        conference.setName(rs.getString("name"));
        conference.setDescription(rs.getString("description"));
        conference.setSlug(rs.getString("slug"));
        conference.setStartedDate(rs.getDate("started_date"));
        conference.setEndedDate(rs.getDate("ended_date"));
        conference.setId(rs.getLong("id"));
        return conference;
    }
    }
    
    There are some simplified queryForXXX methods, which are useful for some specific queries. For example, in order to get the count of the table rows, you can use queryForInt to get the result directly.
    getJdbcTemplate().queryForInt("select count(*) from conference);
    
    And update is more easy when executing a insert, update, delete SQL statement.
    public void delete(final Long id) {
    getJdbcTemplate().update("delete from conference where id=?", id);
    }
    
    The update method can accept a KeyHolder parameter which is use for storing the generated identity key when a insert sql is executed.
    public Long save(final Conference conference) {
    
    final String INSERT_SQL = "insert into conference (id, name, slug, description, started_date, ended_date, version) values (default, ?, ?, ?, ?, ?, 1) ";
    
    KeyHolder generatedKeyHolder = new GeneratedKeyHolder();
    getJdbcTemplate().update(new PreparedStatementCreator() {
    
        @Override
        public PreparedStatement createPreparedStatement(
            Connection connection) throws SQLException {
            PreparedStatement ps = connection.prepareStatement(INSERT_SQL,
                        new String[] { "id" });
                ps.setString(1, conference.getName());
                ps.setString(2, conference.getSlug());
                ps.setString(3, conference.getDescription());
                ps.setTimestamp(4, new java.sql.Timestamp(
                    conference.getStartedDate().getTime()));
                ps.setTimestamp(5, new java.sql.Timestamp(
                    conference.getEndedDate().getTime()));
                return ps;
            }
        }, generatedKeyHolder);
    
    return (Long) generatedKeyHolder.getKey();
    }
    
    batchUpdate is use for batch operations.
    Let us have a look at the parameter classes of these methods.
    PreparedStatementCreator is use for creating a PreparedStatement object from the Jdbc Connection object.
    PreparedStatementSetter is use for filling values into the PreparedStatement placeholder.
    PreparedStatementCallback is use for gathering the result of the execution.
    There are also some method and classes are ready for Stored Procedure operations which are not covered here.
  4. The improved NamedParameterJdbcTemplate.
    As the name indicates, it is an improved version of JdbcTemplate and allow you use named parameters in sql statement instead of the position based placeholder.
    <bean id="namedParamJdbcTemplate"
        class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTempl ate">
    <constructor-arg index="0" ref="dataSource">
    </constructor-arg>
    </bean>
    
    The NamedParameterJdbcTemplate can accept a DataSource bean or a classic JdbcTemplate bean as constructor arguments.
    @Autowired
    NamedParameterJdbcTemplate namedParamJdbcTemplate;
    
    @Override
    public Conference findBySlug(String slug) {
    List<Conference> confs = namedParamJdbcTemplate.query(
            "select * from conference where slug=:slug", 
            new MapSqlParameterSource().addValue("slug", slug),
            new ConferenceMapper());
    if (!confs.isEmpty()) {
        return confs.get(0);
    }
    return null;
    }
    
    Now the query method can accept a SqlParameterSource or a Map as arguments.
    Another improved JdbcDaoSupport version named NamedParameterJdbcDaoSupport is also provided.
    As you see, the improved variant APIs are more friendly for developers.

Hibernate support

Here we will use Hibernate 3 as the example to demonstrate the HibernateDaoSupport API and HibernateTemplate.
NOTE: In Spring 3.1, Spring provides another ~.hibernate4 package to support the new Hibernate 4 API, it does not include DaoSupport and Template at all, Spring recommend developers use the native Hibernate API in new projects, I will demo it in the next posts.
The HibernateDaoSupport and HibernateTemplate are very similar with the Jdbc ones.
  1. Register a DataSource bean, a Hibernate specific SessionFactoryBean bean and declare a HibernateTransactionManager.
    <jdbc:embedded-database id="dataSource">
    </jdbc:embedded-database>
    
    <bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan">
        <list>
            <value>com.hantsylabs.example.spring.model</value>
        </list>
    </property>
    <property name="hibernateProperties">
        <value>
            hibernate.format_sql=true
            hibernate.show_sql=true
            hibernate.hbm2ddl.auto=create
            hibernate.cache.provider_class=org.hibernate.cache.HashtableCacheProvider
        </value>
    </property>
    </bean>
    
    <bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    
  2. Write your implementation class which is extended HibernateDaoSupport class, and inject the SessionFactory bean.
    public class Hibernate3ConferenceDaoImpl 
    extends HibernateDaoSupport 
    implements ConferenceDao {
    
    @Autowired
    public Hibernate3ConferenceDaoImpl( SessionFactory sessionFactory) {
        super();
        setSessionFactory(sessionFactory);
    }
    }
    
    Now you can use HibernateTemplate freely in your implementation class via getHibernateTemplate() method from HibernateDaoSupport.
    Alternatively, you can declare a HiberanteTemplate bean and inject it into your implementation class, it is very similar with the scenario in the Jdbc section.
    <bean id="hibernateTemplate"
    class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    
    Now you are free from subclassing HibernateDaoSupport, only need to implement your own interface.
    public class HibernateTemplateConferenceDaoImpl 
    implements ConferenceDao {
    
    @Autowired
    private HibernateTemplate hibernateTemplate;
    }
    
  3. HibernateTemplate API
    In fact, the methods in HibernateTemplate class are very similar with the ones in Hibernate Session API, but it provides more features, such as it supports Spring exception translator.
    You can use Hibernate Session API in a custom HibernateCallback inner class.
    public Conference findBySlug(final String slug) {
    List<Conference> result = getHibernateTemplate().executeFind(
        new HibernateCallback<List<Conference>>() {
    
        @Override
        public List<Conference> doInHibernate(Session session)
            throws HibernateException, SQLException {
            return session
                    .createQuery("from Conference where slug=:slug")
                    .setParameter("slug", slug)
                    .list();
        }
    });
    
    if (!result.isEmpty()) {
        return  result.get(0);
    }       
    return null;
    }
    
    HibernateCallback includes a single callback method doInHibernate which accept a Hibernate Session as arguments, you can write your own Hibernate query in the method freely.

Jpa Support

Hibernate was proved it is a big success in the latest years in Java communities, and finally most of the Hibernate APIs were standardized as JPA specification 1.0, which is part of Java EE 5. Hibernate 3.2 is one of the JPA 1.0 implementations. NOTE, the reference implementation of JPA specification is EclipseLink.
Some Hibernate concept can be mapped to JPA directly, for example, Session is equivalent to the EntityManager and SessionFactory acts as the same role of EntityManagerFactory.
Spring embraces JPA at the first time when it was released.
It also provides JpaDaoSupport and JpaTemplate classes.
  1. Configure EntityManagerFactory and JpaTransactionManager beans.
    <jdbc:embedded-database id="dataSource">        
    </jdbc:embedded-database>
    
    <bean
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
    id="entityManagerFactory">
    <property name="persistenceUnitName" value="persistenceUnit" />
    <property name="dataSource" ref="dataSource" />
    <property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"></property>
    <property name="packagesToScan">
        <array>
            <value>com.hantsylabs.example.spring.model</value>
        </array>
    </property>
    <property name="jpaProperties">
        <value>
        hibernate.format_sql=true
        hibernate.show_sql=true
        hibernate.hbm2ddl.auto=create
        </value>
    </property>
    </bean>
    
    <bean class="org.springframework.orm.jpa.JpaTransactionManager"
    id="transactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    
  2. Use JpaTemplate freely in your implementation class.
    public class JpaConferenceDaoImpl 
    extends JpaDaoSupport
    implements ConferenceDao {
    
    @Autowired
    public JpaConferenceDaoImpl(EntityManagerFactory emf) {
        super();
        setEntityManagerFactory(emf);
    }
    
    @Override
    public Conference findById(Long id) {
        return (Conference) getJpaTemplate().find(Conference.class, id);
    }
    }
    
    JpaTemplate almost copies the methods from EntityManager.
    NOTE: In Spring 3.1, the JpaDaoSupport is marked as @Deprecated and not recommended in new projects. In the future posts, I will demonstrate how to use the native JPA API.

    Please check out the source codes from my Github.com, https://github.com/hantsy/spring-sandbox
发表评论

此博客中的热门博文

JPA 2.1: Attribute Converter

JPA 2.1: Attribute Converter If you are using Hibernate, and want a customized type is supported in your Entity class, you could have to write a custom Hibernate Type. JPA 2.1 brings a new feature named attribute converter, which can help you convert your custom class type to JPA supported type. Create an Entity Reuse thePostentity class as example. @Entity @Table(name="POSTS") public class Post implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name="ID") private Long id; @Column(name="TITLE") private String title; @Column(name="BODY") private String body; @Temporal(javax.persistence.TemporalType.DATE) @Column(name="CREATED") private Date created; @Column(name="TAGS") private List<String> tags=new ArrayList<>(); } Create an attribute converter In this example…

Auditing with Hibernate Envers

Auditing with Hibernate Envers The approaches provided in JPA lifecyle hook and Spring Data auditing only track the creation and last modification info of an Entity, but all the modification history are not tracked. Hibernate Envers fills the blank table. Since Hibernate 3.5, Envers is part of Hibernate core project. Configuration Configure Hibernate Envers in your project is very simple, just need to addhibernate-enversas project dependency. <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-envers</artifactId> </dependency> Done. No need extra Event listeners configuration as the early version. Basic Usage Hibernate Envers provides a simple@Auditedannotation, you can place it on an Entity class or property of an Entity. @Audited private String description; If@Auditedannotation is placed on a property, this property can be tracked.

Spring Data Mongo: bridge MongoDB and Spring

Spring Data Mongo: bridge MongoDB and SpringMongoDBis one of the most popular NoSQL products, Spring Data Mongo(Maven archetype id is spring-data-mongodb) tries to provides a simple approach to access MongoDB. Configuration Add the following code fragments in your Spring configuration. <!-- Mongo config --> <mongo:db-factory id="mongoDbFactory" host="localhost" port="27017" dbname="conference-db" /> <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> <constructor-arg ref="mongoDbFactory" /> </bean> <mongo:repositories base-package="com.hantsylabs.example.spring.mongo" /> Firstly, declare aDbFactorywhich is responsible for connecting to the MongoDB server. Then, register theMongoTemplateobject which envelop the data operations on MongoDB. Lastly addmongo:repositorieswith an essentialbase-packageattribute, Spring will discovery your d…