Spring4:JDBC
数据库连接池
对一个简单的数据库应用,由于对数据库的访问不是很频繁,这时可以简单地在需要访问数据库时,就新创建一个连接,就完后就关闭它,这样做也不会带来什么性能上的开销。但是对于一个复杂的数据库应用,情况就完全不同而,频繁的建立、关闭连接,会极大地减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。
通过建立一个数据库连接池以及一套连接使用管理策略,可以达到连接复用的效果,使得一个数据库连接可以得到安全、高效的复用,避免了数据库连接频繁建立、关闭的开销。
数据库连接池的基本原理是在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。如:外部使用者可通过getConnection方法获取连接,使用完毕后再通过releaseConnection方法将连接返回,注意此时连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。
数据库连接池技术带来的好处:
1、资源重用
由于数据库连接得到重用,避免了频繁创建、释放链接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进行/线程数量)
2、更快地系统响应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接池置于池中备用。此时连接的初始化工作均已完成,对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间
3、统一的连接管理,避免数据库连接泄露
在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露。
目前数据库连接池产品是非常多的,主要有:
1、dbcp
dbcp,即DataBase Connection PoolApache出品,Spring开发组推荐使用的数据库连接池,开发较为活跃,是一个使用极为广泛的数据库连接池产品。不过从网上
2、c3p0
Hibernate开发组推荐使用的数据库连接池,它实现了数据源和JNDI的绑定
3、Proxool
Proxool的口碑较好,没什么负面评价(比如dbcp就是因为Hibernate认为它BUG太多Hibernate才不推荐使用的),也是Hibernate开发组推荐使用的数据库连接池,不过使用者不算多,开发不够活跃。这个连接池提供了连接池监控的功能,方便易用,便于发现连接池泄露的情况
基于Spring的JDBC基本框架搭建
先讲一下使用Spring实现JDBC,数据库连接池就用Spring开发组推荐的DBCP,DBCP需要三个jar包,先下载一下:
1、commons-dbcp-1.4.jar,官方网站上有,点我下载
2、commons.pool-1.6.jar,官方网站上有,点我下载
3、commons.collections4-4.0.jar,官方网站上有,点我下载
下载了这三个jar包之后请导入自己的工程中(注意MySql的包别忘记导入了),虽然dbcp和pool都出了dbcp2和pool2的版本,Apache官网上都可以下载,但是这里提供的还是dbcp1和pool1的版本下载地址,一个原因是dbcp2和pool2都只可以在JDK1.7及JDK1.7以上的版本运行,而dbcp1和pool1则可以在JDK1.6的版本运行,考虑到MyEclipse10默认自带的JRE就是1.6版本的,所以这里下载、使用dbcp1和pool1。想要dbcp2和pool2的可以自己去Apache官网下载,不过要注意,dbcp2必须和pool2一组,dbcp1必须和pool1一组,不可以混着用。
JDBC,我之前写过一篇文章,数据库建立和实体类都是用的原文章里面的,这里只是把原生的JDBC搬到Spring JDBC下而已,看下最基础的写法,再添加功能,学生管理类为:
public class StudentManager { private JdbcTemplate jdbcTemplate; private static StudentManager instance = new StudentManager(); public static StudentManager getInstance() { return instance; } public JdbcTemplate getJdbcTemplate() { return jdbcTemplate; } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }
Spring的XML配置文件命名为jdbc.xml,jdbc.xml的写法为:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd"> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <!-- 驱动包名 --> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <!-- 数据库地址 --> <property name="url" value="jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8;" /> <!-- 用户名 --> <property name="username" value="root" /> <!-- 密码 --> <property name="password" value="root" /> <!-- 最大连接数量 --> <property name="maxActive" value="150" /> <!-- 最小空闲连接 --> <property name="minIdle" value="5" /> <!-- 最大空闲连接 --> <property name="maxIdle" value="20" /> <!-- 初始化连接数量 --> <property name="initialSize" value="30" /> <!-- 连接被泄露时是否打印 --> <property name="logAbandoned" value="true" /> <!-- 是否自动回收超时连接 --> <property name="removeAbandoned" value="true" /> <!-- 超时等待时间(以秒为单位) --> <property name="removeAbandonedTimeout" value="10" /> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="studentManager" class="com.xrq.jdbc.StudentManager" factory-method="getInstance"> <property name="jdbcTemplate" ref="jdbcTemplate" /> </bean> </beans>
主函数为:
public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("jdbc.xml"); System.out.println(StudentManager.getInstance()); System.out.println(StudentManager.getInstance().getJdbcTemplate()); }
运行没什么问题,得到StudentManager的引用地址和StudentManager中属性jdbcTemplate的引用地址,说明整个连接、注入都没问题。
JDBCTemple是Spring里面最基本的JDBC模板,利用JDBC和简单的索引参数查询提供对数据库的简单访问。除了JDBCTemplate,Spring还提供了NamedParameterJdbcTemplate和SimpleJdbcTemplate两个类,前者能够在执行查询时把值绑定到SQL里的命名参数而不是使用索引,后者利用Java 5的特性比如自动装箱、泛型和可变参数列表来简化JDBC模板的使用。具体使用哪个看个人喜好,这里使用JdbcTemplate,所以把JdbcTemplate添加到学生管理类中。
另外:
1、dbcp提供很多参数给用户配置,每个参数的意思都以注释的形式写在.xml里面了,更加具体要知道每个参数的意思可以上网查询一下
2、注意dbcp的属性url,url指的是数据库连接地址,遇到特殊字符需要转义,所以这里的"&"变成了"&",不然会报错
基于Spring的JDBC增删改查
上面一部分搭建了一个Spring JDBC的基础框架,下面看一下Java代码如何实现CRUD,这个过程中jdbc.xml都不需要变化。
1、添加一个学生信息,代码为:
// 添加学生信息 public boolean addStudent(Student student) { try { jdbcTemplate.update("insert into student values(null,?,?,?)", new Object[]{student.getStudentName(), student.getStudentAge(), student.getStudentPhone()}, new int[]{Types.VARCHAR, Types.INTEGER, Types.VARCHAR}); return true; } catch (Exception e) { return false; } }
2、根据Id删除指定学生信息,代码为:
// 根据Id删除单个学生信息 public boolean deleteStudent(int id) { try { jdbcTemplate.update("delete from student where studentId = ?", new Object[]{id}, new int[]{Types.INTEGER}); return true; } catch (Exception e) { return false; } }
3、根据Id更新学生信息,代码为:
// 根据Id更新指定学生信息 public boolean updateStudent(int Id, Student student) { try { jdbcTemplate.update("update student set studentName = ?, studentAge = ?, studentPhone = ? where studentId = ?", new Object[]{student.getStudentName(), student.getStudentAge(), student.getStudentPhone(), Id}, new int[]{Types.VARCHAR, Types.INTEGER, Types.VARCHAR, Types.INTEGER}); return true; } catch (Exception e) { return false; } }
4、根据Id查询学生信息,代码为:
// 根据学生Id查询单个学生信息 public Student getStudent(int id) { try { return (Student)jdbcTemplate.queryForObject("select * from student where studentId = ?", new Object[]{id}, new int[]{Types.INTEGER}, new RowMapper<Student>(){ public Student mapRow(ResultSet rs, int arg1) throws SQLException { Student student = new Student(rs.getInt(1), rs.getString(2), rs.getInt(3), rs.getString(4)); return student; } }); } // 根据Id查询学生信息抛异常, 不管什么原因, 认为查询不到该学生信息, 返回null catch (DataAccessException e) { return null; } }
5、查询所有学生信息,代码为:
// 查询所有学生信息 public List<Student> getStudents() { List<Map<String, Object>> resultList = jdbcTemplate.queryForList("select * from student"); List<Student> studentList = null; if (resultList != null && !resultList.isEmpty()) { studentList = new ArrayList<Student>(); Map<String, Object> map = null; for (int i = 0; i < resultList.size(); i++) { map = resultList.get(i); Student student = new Student( (Integer)map.get("studentId"), (String)map.get("studentName"), (Integer)map.get("studentAge"), (String)map.get("studentPhone") ); studentList.add(student); } } return studentList; }
这就是简单的CRUD操作,有了这5个作为基础,其余都可以在这5个的基础上做扩展,就不继续详细写下去了,说几个注意点:
1、个人使用经验来说,除了最后一个查询所有的以外,建议给其他的都加上try...catch...块,因为在操作失败的时候会抛出异常,捕获了就可以知道这次的操作失败了,否则只能程序终止,而自己也不知道操作到底是成功还是失败
2、添加信息、更新信息不建议把每个待操作的字段都作为形参而建议形参就是一个Student实体类,这样一来更符合面向对象的设计原则,二来形参列表的字段多很容易导致出错
3、update、query方法,如果有占位符?的话,建议选择有参数类型的重载方法,指定每个占位符的字段类型,就像我上面代码写的那样
最后,这里讲的都是jdbcTemplate的基本使用,jdbcTemplate里面还有很多方法,就不一一细说了,可以自己去试一下,也可以查阅Spring API文档。
读取配置文件中的数据
之前我们都是把数据库连接的一些属性配置在db.properties里面的,这样方便修改,而这里却是在jdbc.xml里面写死的,所以要想一个办法怎么可以从db.properties里面读取出配置,context帮助开发者实现了这一点,看一下jdbc.xml如何写:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 7 http://www.springframework.org/schema/context 8 http://www.springframework.org/schema/context/spring-context-4.2.xsd"> 9 10 <context:property-placeholder location="classpath:db.properties"/> 11 12 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> 13 <!-- 驱动包名 --> 14 <property name="driverClassName" value="${mysqlpackage}" /> 15 <!-- 数据库地址 --> 16 <property name="url" value="${mysqlurl}" /> 17 <!-- 用户名 --> 18 <property name="username" value="${mysqlname}" /> 19 <!-- 密码 --> 20 <property name="password" value="${mysqlpassword}" /> 21 <!-- 最大连接数量 --> 22 <property name="maxActive" value="150" /> 23 <!-- 最小空闲连接 --> 24 <property name="minIdle" value="5" /> 25 <!-- 最大空闲连接 --> 26 <property name="maxIdle" value="20" /> 27 <!-- 初始化连接数量 --> 28 <property name="initialSize" value="30" /> 29 <!-- 连接被泄露时是否打印 --> 30 <property name="logAbandoned" value="true" /> 31 <!-- 是否自动回收超时连接 --> 32 <property name="removeAbandoned" value="true" /> 33 <!-- 超时等待时间(以秒为单位) --> 34 <property name="removeAbandonedTimeout" value="10" /> 35 </bean> 36 37 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 38 <property name="dataSource" ref="dataSource" /> 39 </bean> 40 41 <bean id="studentManager" class="com.xrq.jdbc.StudentManager" factory-method="getInstance"> 42 <property name="jdbcTemplate" ref="jdbcTemplate" /> 43 </bean> 44 </beans>
重点就是第10行,第14、第16、第18、第20行就是取属性的写法,和前端的FTL语言类似。
我不能保证写的每个地方都是对的,但是至少能保证不复制、不黏贴,保证每一句话、每一行代码都经过了认真的推敲、仔细的斟酌。每一篇文章的背后,希望都能看到自己对于技术、对于生活的态度。
我相信乔布斯说的,只有那些疯狂到认为自己可以改变世界的人才能真正地改变世界。面对压力,我可以挑灯夜战、不眠不休;面对困难,我愿意迎难而上、永不退缩。
其实我想说的是,我只是一个程序员,这就是我现在纯粹人生的全部。
==================================================================================