SpringJDBC

来源:互联网 发布:淘宝里面主播挣多少钱 编辑:程序博客网 时间:2024/05/22 13:14

SpringJDBC是对原生JDBC的一个封装,它不是一个ORM框架,但是对于一些小项目来说,它的所提供的功能就已经卓卓有余了,而且对于一些比价复杂的SQL 原声的JDBC还是有它一定的优势,所以今天我就将SpringJDBC的知识写一下。

首先Spring为各种持久化技术提供模板类,这样做有两点,第一点方面操作,第二点你让hibernate的session和connection等这些线程不安全的类 变成了线程安全了(因为使用了ThreadLocal类),这些就不在这里讨论了,如果对这方面有兴趣可以看一下《Spring 3.X企业应用开发实战》。

使用SpringJDBC的前提地下要配置数据源,下面我就列出数据源的完整配置,当然在配置文件中不需要将所有属性都设置上。

以下就是datasource.properties 的属性配置(这使用的是BasicDataSource 数据源,在Spring ORM 章节 我会采用 C3P0的 数据源 会有更加详细的C3P0配置):

driverClassName=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306/springreviewusername=rootpassword=tonyyan#连接池创建的连接的默认auto-commit状态(默认值:true)defalutAutoCommit=true#连接池创建的连接的默认的read-only状态。如果没有设置则setReadOnly方法将不会被调用 (默认值:驱动默认)defaultReadOnly=false#连接池创建的连接的默认的TransactionIsolation状态,可选值如下(默认值:驱动默认):#NONE  READ_COMMITTED READ_UNCOMMITTED REPEATABLE_READ SERIZLIZABLEdefaultTransactionIsolation=READ_COMMITTED#初始化连接,连接池启动时创建的初始化连接数量(默认值:0)initialSize=0#最大活动连接,连接池启动时创建的初始化连接数量(默认值:8)maxActive=8#最大空闲连接,连接池中容许保持空闲状态的最大连接数,超过的空闲连接将被释放,如果设置为负数表示不限制(默认值:8)maxIdle=8#最小空闲连接,连接池中容许保持空闲状态的最小连接数,超过的空闲连接将被释放,如果设置为负数表示不限制(默认值:0)minIdle=0#最大等待事件,当没有可用连接时,连接池等待连接被归还的最大时间(毫秒为单位),超过事件则抛出异常,如果设置-1表示无线等待(默认值:无限)maxWait=1500#SQL查询语句。用于连接有消息检查,必须放回至少一行的数据MYSQL中可以设置select 1 oracle可以设置select 1 from dualvalidationQuery=select 1#指明是否在从连接池中取出连接并连接前前进行检查,如果检查失败,则从连接池删除该连接并尝试取出另外一个新连接(默认值:TRUE) 必须设置validationQuerytestOnBorrow=false#指明是否在连接使用完毕后将连接归还给连接池是进行有效性检查,如果失败,则从连接池中删除该连接(默认值:FALSE) 必须设置validationQuerytestOnReturn=false#指明连接是否被空闲回收器进行检查,如果检查失败,则连接将从池中去除(默认值:FALSE) 必须设置validationQuerytestWhileIdle=true#空闲连接回收器线程运行的周期(毫秒为单位)如果设置非整数,则不运行。(默认值:-1) 必须设置validationQuerytimeBetweenEvictionRunsMillis=3600000#每次空闲连接回收器线程运行时检查的连接数量numTestsPerEvictionRun=3#连接在可被空闲接连回收器收前已经在池中空闲的时间(毫秒为单位)(默认值:1000*60*30)minEvictableIdleTimeMillis=1800000#开启池的prepared statement 池功能,设置为true CallableStatement 和 PreparedStatemen 都会被缓存起来(默认值:FALSE)poolPreparedStatements=false#PreparedStatement池能够同时分配的打开的statements的最大数量,如果设置为0表示不限制(默认值:无限)maxOpenPreparedStatements=0#标记是否删除泄露的连接,如果为true则如果可能存在泄露的连接将会被主动清除,#这个机制在(getNumIdle()<2 and (getNumActive() > getMaxActive()-3))条件下触发。removeAbandoned=false#泄露的连接可以被回收的超时时间(秒为单位)(默认值:300)removeAbandonedTimeout=72000#标记当Statement或者连接被泄露时是否打印程序的stack traces日志。#被泄露的statement和连接的日志添加在每个连接打开或者生成的statement,因为需要生成stack tracelogAbandoned=false

以下是Spring的配置文件:

<beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:p="http://www.springframework.org/schema/p"       xmlns:context="http://www.springframework.org/schema/context"       xmlns:tx="http://www.springframework.org/schema/tx"       xmlns:aop="http://www.springframework.org/schema/aop"       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans.xsd       http://www.springframework.org/schema/aop       http://www.springframework.org/schema/aop/spring-aop.xsd       http://www.springframework.org/schema/tx       http://www.springframework.org/schema/tx/spring-tx.xsd       http://www.springframework.org/schema/context       http://www.springframework.org/schema/context/spring-context.xsd">    <context:property-placeholder location="datasource.properties"/>    <context:component-scan base-package="com.maxfunner"/>    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">        <property name="driverClassName" value="${driverClassName}"/>        <property name="url" value="${url}"/>        <property name="username" value="${username}"/>        <property name="password" value="${password}"/>    </bean>    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dataSource"/>    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"          p:dataSource-ref="dataSource"/>    <aop:config>        <aop:pointcut id="allServices" expression="within(com.maxfunner.service..*)" />        <aop:advisor advice-ref="txAdvice" pointcut-ref="allServices" />    </aop:config>    <tx:advice id="txAdvice" transaction-manager="transactionManager">        <tx:attributes>            <tx:method name="*" read-only="false" isolation="READ_COMMITTED" />        </tx:attributes>    </tx:advice></beans>

当然我都配置了Spring的声明式事务,将service所有类都进行声明式事务的织入。

以下将Spring的模板类和所有事物管理器(transaction manager)列出:


Template:

JDBC ---> org.springframework.jdbc.core.JdbcTemplate

Hibernate --->org.springframework.orm.hibernate5.HibernateTemplate (这里是hibernate5的template spring支持从hibernate3开始的所有template)

mybatis在mybatis的包中提供,其他我没有用过的就不写了。。。


PlatformTransactionManager 的实现类如下:

org.springframework.orm.hibernate5.HibernateTransactionManager  (这里是hibernate5的template spring支持从hibernate3开始的所有TransactionManager)

org.springframework.jdbc.datasource.DataSourceTransactionManager (SpringJDBC和mybatis使用这个)

其他jdo 和 JTA 我就不写了。。。


以下是一个使用SpringJDBC 创建表插入数据的实例:

@Repositorypublic class UserDao {    @Autowired    private JdbcTemplate template;    public JdbcTemplate getTemplate() {        return template;    }    public void setTemplate(JdbcTemplate template) {        this.template = template;    }    public void initUserTable(){        String createTableSql = "create table t_user(user_id int primary key auto_increment,username varchar(100),pwd varchar(100))";        String dropOldTableSQL = "drop table if exists t_user";        //使用JdbcTemplate执行删除原表        template.execute(dropOldTableSQL);        //使用JdbcTemplate创建表        template.execute(createTableSql);        //使用GeneratedKeyHolder获得新插入数据的主键        KeyHolder keyHolder = new GeneratedKeyHolder();        final String insertUserSQL = "insert into t_user(username,pwd) VALUES (?,?)";        final User adminUser = new User("Admin","adminPwd");        //使用JdbcTemplate去更新数据        template.update(new PreparedStatementCreator() {            public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {                //通过connection获得PreparedStatement 同时指定需要返回主键RETURN_GENERATED_KEYS                PreparedStatement statement = connection.prepareStatement(insertUserSQL, Statement.RETURN_GENERATED_KEYS);                //传入参数                statement.setString(1,adminUser.getUsername());                statement.setString(2,adminUser.getPwd());                return statement;            }        },keyHolder);        //输出保存插入数据的主键到对象当中        adminUser.setUserId(keyHolder.getKey().intValue());                System.out.println("adminUser ->>>>>> " + adminUser);        //需要批量处理的List        final List<User> userList = new ArrayList<User>();        userList.add(new User("TONY","TONYPWD"));        userList.add(new User("asd","TONYPWD"));        userList.add(new User("sds","TONYPWD"));        userList.add(new User("fvcx","TONYPWD"));        userList.add(new User("xwd","TONYPWD"));        userList.add(new User("dfsd","TONYPWD"));        userList.add(new User("cew","TONYPWD"));        userList.add(new User("adfd","TONYPWD"));        userList.add(new User("sdfwe","TONYPWD"));                //使用JdbcTemplate的批量插入        template.batchUpdate(insertUserSQL, new BatchPreparedStatementSetter() {            public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {                preparedStatement.setString(1,userList.get(i).getUsername());                preparedStatement.setString(2,userList.get(i).getPwd());            }            public int getBatchSize() {                return userList.size();            }        });    }}

使用查询数据之RowMapper处理数据集实例:

public List<User> getAllUser(){    String sql = "select user_id,username,pwd from t_user where user_id between ? and ?";    List<User> userList = template.query(sql, new Object[]{1, 10}, new RowMapper<User>() {        public User mapRow(ResultSet resultSet, int i) throws SQLException {            User user = new User();            user.setUserId(resultSet.getInt(1));            user.setUsername(resultSet.getString(2));            user.setPwd(resultSet.getString(3));            return user;        }    });    return userList;}

使用查询数据之RowCallbackHandler处理数据集:

public List<User> getAllUser() {    String sql = "select user_id,username,pwd from t_user where user_id between ? and ?";    final List<User> userList = new ArrayList<User>();    template.query(sql, new Object[]{1, 10}, new RowCallbackHandler() {        public void processRow(ResultSet resultSet) throws SQLException {            User user = new User();            user.setUserId(resultSet.getInt(1));            user.setUsername(resultSet.getString(2));            user.setPwd(resultSet.getString(3));            userList.add(user);        }    });        return userList;}

使用JdbcTemplate查询单条数据:

public int getUserCount() {    String countSQL = "select count(*) from t_user";    return this.template.queryForObject(countSQL, new RowMapper<Integer>() {        public Integer mapRow(ResultSet resultSet, int i) throws SQLException {            return resultSet.getInt(1);        }    });}

使用SpringTemplate 调用存储过程:


以下是我在MySQL创建的存储过程

delimiter //
create procedure P_USER_NAME(IN in_user_id int , OUT out_name varchar(100))
begin
select username into out_name  from t_user where user_id = in_user_id;
end


使用SpringTemplate进行调用:

public String getUserNameById(final int userId){    String sql = "{call P_USER_NAME(?,?)}";    String username = this.template.execute(sql, new CallableStatementCallback<String>() {        public String doInCallableStatement(CallableStatement callableStatement) throws SQLException, DataAccessException {            callableStatement.setInt(1,userId);            callableStatement.registerOutParameter(2,Types.VARCHAR);            callableStatement.execute();            return callableStatement.getString(2);        }    });    return username;}

当然我们可以返回一个ResultSet行集数据 但是我们返回的是Spring提供的SqlRowSet,用法基本上和resultSet差不多,但是需要注意的是resultSet是连接性行集,就是说是在连接过程中使用的行集合,但是Spring提供的是SqlRowSet是非连接性行集,所以不会像resultSet一样采取分批返回数据,sqlRowSet会一次性放回所有的行集(fetchsize)所以需要特别注意,如果一般小心就会导致JVM的内存销毁压力山大。以下是关于SqlRowSet的用法:

public SqlRowSet getAllUserRowSet(){    String sql = "select user_id,username,pwd from t_user ";    SqlRowSet rowSet = this.template.queryForRowSet(sql);    while (rowSet.next()) {        System.out.println("user_id : " + rowSet.getInt(1) +                " , name : " + rowSet.getString(2) +                " , pwd : " + rowSet.getString(3));    }    return rowSet}

我们还可以使用另外一种JDBCTemplate去完成操作,NamedParameterJdbcTemplate。

使用NamedParameterJdbcTemplate 可以不使用?这种占位符,可以对占位符进行命名,减少出错机会。而且还可以BeanPropertySqlParameterSource将目标类的属性名与参数名进行对应,从而可以自动传参数,看看以下例子:

public void insertUser(User user){    String sql = "insert into t_user(username,pwd) values(:username,:pwd)";    SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(user);    namedParameterJdbcTemplate.update(sql,parameterSource);}

其中User类有三个属性 userId,username,pwd 所以BeanPropertySqlParameterSource自动取对象中的username 和 pwd值,但是如果要使用NamedParameterJdbcTemplate也需要在配置文件进行配置,配置如下:

<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">    <const

如果不使用BeanPropertySqlParameterSource还可以用以下方法进行相同的操作:

public void insertUser(User user){    String sql = "insert into t_user(username,pwd) values(:username,:pwd)";    Map<String,Object> params = new HashMap<String, Object>();    params.put("username",user.getUsername());    params.put("pwd",user.getPwd());    namedParameterJdbcTemplate.update(sql,params);}
可以看见我只使用了一个Map作为一个参数传入了 template的update方法当中,当然也可以使用MqlSqlParameterSource这个跟直接使用Map差别不大。


以OO方式访问数据库,就不再这里说了,因为对于我本人来说以上几种才是我用的比较多的方式,如果兴趣的读者可以参考《Spring 3.X企业应用开发实战》(近期看见出了4.x)我个人觉得这本书把spring的很多东西都说的很详细,如果有其他读者觉得有其他好书也可以在评论中评论跟我分享。


1 0