#数据访问java
##DAOmysql
##ORMspring
#Why Spring JDBC? sql
##数据访问 使用JDBC的方式去访问数据的过程的步骤数据库
这些步骤里面,业务里面真正须要关心的是链接参数:必须去关心,没有链接参数就没办法进行链接;声明SQL语句与参数:直接和业务需求相关,例如查询某种用户的结果集,或者符合某种条件的结果集,不声明SQL没法执行结果;执行业务:循环访问结果对于用户来讲,若是有某种方式来告诉每行记录都是什么样子的,只要根据结果集作执行对应的业务就能够。
从用户的角度来看,只须要作这三样事情,就能够去完成业务需求,对于其余的底层实现,如何打开链接、如何访问结果、如何处理异常关闭资源,从用户角度来讲并无对业务产生很大的影响。Spring JDBC的做用就是封装这些用户不关心的底层细节内容。只要关心的内容,经过接口暴露出来,只须要关心编写对应的业务。Spring JDBC提升编程效率,关系业务细节。apache
##DataSource Spring里面是使用DataSource类接口进行表明的。DataSource表明一个数据源,以下内容就是数据源所须要的参数。编程
经过这几个参数能够构成一个DataSource,根据特定的需求定义成不一样的数据源,好比能够定义成MySQL数据源,也能够定义成Oracle的数据源。app
DataSource方法惟一的基础方法就是 getConnection,好比咱们创建一个DataSource的链接,咱们获取链接之后就能够作数据查询相关的事情,例如查询SQL,建立表框架
##DataSource DataSource是接口,并非由Spring提供的,而是由Java自己的JavaEE提供的接口,而Spring只是在这个基础之上提供了一个实现DriverManagerDatasource
,基础JDBC提供的DataSource。可是这个实现有一个问题,就是每次使用时都会建立一次链接。在一些场景下,例如频繁查询都会建立一次链接,从性能上来说并非很好,因此就会有相似线程池同样的数据库链接池的概念,可是这个Spring自己并无提供,而是由Apache Commons DBCP提供的BasicDataSource
的实现。BasicDataSource
是在org.apache.commons.dbcp
包下实现类。BasicDataSource
实现了数据库链接池的功能,这样就不须要每次查询的时候,都去建立一个链接,实现链接的复用maven
##BasicDataSource配置 BasicDataSource配置模板
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="testOnBorrow" value="false" /> <property name="testWhileIdle" value="true" /> <!-- 链接池启动时的初始值 --> <property name="initialSize" value="10" /> <!-- 链接池的最大值 --> <property name="maxActive" value="100" /> <!-- 最大空闲值.当通过一个高峰时间后,链接池能够慢慢将已经用不到的链接慢慢释放一部分,一直减小到maxIdle为止 --> <property name="maxIdle" value="50" /> <!-- 最小空闲值.当空闲的链接数少于阀值时,链接池就会预申请去一些链接,以避免洪峰来时来不及申请 --> <property name="minIdle" value="10" /> <!--#给出一条简单的sql语句进行验证--> <property name="validationQuery" value="select getdate()" /> <!--#在取出链接时进行有效验证--> <property name="removeAbandonedTimeout" value="120" /> <property name="removeAbandoned" value="true" /> <!-- #运行判断链接超时任务的时间间隔,单位为毫秒,默认为-1,即不执行任务。 --> <property name="timeBetweenEvictionRunsMillis" value="3600000" /> <!-- #链接的超时时间,默认为半小时。 --> <property name="minEvictableIdleTimeMillis" value="3600000" /> </bean> </beans>
经过db.properties获取数据库链接
<context:property-placeholder location="db.properties" />
上面这种方式是比较简单的加载方式,效果等同于下面的方式
<bean id = "headerProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigure"> <property name="location" value="classpath:db.properties" /> </bean>
##JdbcTemplate 声明SQL及参数、执行SQL及处理结果、异常处理都封装在叫作JdbcTemplate
的类里面,所在的包为org.springframework.jdbc.core
。
##JdbcTemplate与JDBC差异 例如,下面的代码查询了SQL有多少行,这里经过queryForObject方法执行SQL语句,并把结果集转换成Integer.class。
String sql = "SELECT COUNT(*) FROM user"; int rowCount = this.jdbcTemplate.queryForObject(sql, Integer.class);
也能够查询某批用户的数量,这里能够传入SQL的参数
String sql = "SELECT COUNT(*) FROM user WHERE frist_name = ?"; int countOfNamedJoe = this.jdbcTemplate.queryForObject(sql,Integer.class,"Joe");
咱们还能够插入其余类型,好比查找某个用户id的lastName是什么,你们能够看到,咱们把参数放到了Object[]当中。
String sql = "SELECT COUNT(*) FROM user WHERE id = ?"; String lastName = this.jdbcTemplate.queryForObject(sql,new Object[]{1212L},String.class);
**注意:**上面两个例子的参数放置是相反的, JdbcTemplate提供了不少查询相关插入相关的接口,通常都会具备两种不一样类型的接口,好比返回参数类型放到前面查询参数放到后面,另一种恰好相反。 主要缘由是由于Java函数获取不肯定参数个数的时候,是在最后的参数添加...
##JdbcTemplate增改删方法 ###新增,插入
String sql = "INSERT INTO user (first_name,last_name) values (? ,?)"; this.jdbcTemplate.update(sql,"Meimei","Han");
###更新
String sql = "UPDATE user SET last_name=? WHERE id = ?"; this.jdbcTemplate.update(sql, "Li",5276L);
###删除
String sql = "DELETE FROM user WHERE id = ?"; this.jdbcTemplate.update(sql, Long.valueOf(userId));
###建立表格
String sql = "create table user (id integer,first_name varchar(100),last_name varchar(100))"; this.jdbcTemplate.execute(sql);
##JdbcTemplate 转换对象 ORM是把数据库的信息转换成对象,JdbcTemplate也是能够完成该功能的。
JdbcTemplate提供了接口叫作RowMapper,能够经过RowMapper把ResultSet一一的转换成你所须要的对象,例以下面的例子,只有一行数据,id为1212的用户,返回了User的Java对象。
String sql = "SELECT first_name,last_name FROM user WHERE id = ?"; User user = this.jdbcTemplate.queryForObject(sql,new Object[]{1212L},new RowMapper<User>(){ public User mapRow(ResultSet rs,int rowNum) throws SQLException{ User user = new User(); user.setFirstName(rs.getString("first_name")); user.setLastName(rs.getString("last_name")); } });
也能够像以下方法返回List<User>
String sql = "SELECT first_name,last_name FROM user"; List<User> users = this.jdbcTemplate.queryForObject(sql,new RowMapper<User>(){ public User mapRow(ResultSet rs,int rowNum) throws SQLException{ User user = new User(); user.setFirstName(rs.getString("first_name")); user.setLastName(rs.getString("last_name")); } });
###总结JdbcTemplate使用起来比较简单,没有打开链接,转换结果集,关闭链接,释放资源等操做。
##定义JdbcTemplate ###Java代码形式定义JDBCTemplate 在设置DataSource的时候建立JdbcTemplate
public class JdbcExampleDao implements ExampleDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource){ this.jdbcTemplate = new JdbcTemplate(dataSource); } // ... DAO 接口实现 }
###XML声明使用JdbcTemplate
<bean id="exampleDao" class="com.netease.course.JdbcExampleDao" > <property name= "dataSource" ref="dataSource" /> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <context:property-placeholder location="jdbc.properties" />
###经过Annotation(注解)方式配置JdbcTemplate @Repository这个表明是DAO的Bean,是对数据进行了访问,@Autowired来进行自动的注入
@Repository public class JdbcExampleDao implements ExampleDao { private JdbcTemplate jdbcTemplate; @Autowired public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } // ... DAO 接口实现 }
##代码演示JdbcTemplate的使用 maven的基本依赖,jdbcTemplate的基本依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.3.RELEASE</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.39</version> </dependency>
添加数据库配置文件db.properties
jdbc.driverClassName= com.mysql.jdbc.Driver jdbc.url= jdbc:mysql://192.168.1.200:3306/example jdbc.username=root jdbc.password=
在Spring配置文件配置数据源
<context:component-scan base-package="com.hava.spring_data" /> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <context:property-placeholder location="db.properties" />
测试用的DAO
package com.hava.spring_data.repository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import javax.sql.DataSource; /** * Created by yanfa on 2016/10/24. */ @Repository public class MyJdbcTemplateDao { private JdbcTemplate jdbcTemplate; @Autowired public void setDataSource(DataSource dataSource){ this.jdbcTemplate = new JdbcTemplate(dataSource); } public void createTable() { System.out.println("ceateTable"); String sql = "CREATE TABLE user (id INTEGER,first_name VARCHAR(100), last_name VARCHAR(100))"; jdbcTemplate.execute(sql); } }
主执行类
package com.hava.spring_data; import com.hava.spring_data.repository.MyJdbcTemplateDao; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by yanfa on 2016/10/24. */ public class SpringJdbcMain { public static void main(String[] args) throws Exception { ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml"); MyJdbcTemplateDao jdbcTemplateDao = context.getBean("myJdbcTemplateDao", MyJdbcTemplateDao.class); jdbcTemplateDao.createTable(); ((ConfigurableApplicationContext) context).close(); } }
执行时发生异常以下:
java.sql.SQLException: Access denied for user 'root'@'192.168.1.100' (using password: NO)
因为密码错误,更正密码则运行正确
jdbcTemplate插入数据
public void insertData(){ this.jdbcTemplate.update("INSERT INTO user VALUES (1, ?, ?)", "Meimie","Han"); this.jdbcTemplate.update("INSERT INTO user VALUES (1, ?, ?)", "Lei","Li"); }
jdbcTemplate获取行数
public int count(){ String sql = "SELECT COUNT(*) FROM user"; return this.jdbcTemplate.queryForObject(sql,Integer.class); }
执行结果
ceateTable 2
##SQL结果转换成Java对象 Entity对象类
package com.hava.spring_data.entity; /** * Created by yanfa on 2016/10/24. */ public class User { private int id; private String first_name; private String last_name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getFirst_name() { return first_name; } public void setFirst_name(String first_name) { this.first_name = first_name; } public String getLast_name() { return last_name; } public void setLast_name(String last_name) { this.last_name = last_name; } }
jdbcTemplate查询
public List<User> getUserList(){ return this.jdbcTemplate.query("SELECT * FROM user", new RowMapper<User>() { @Override public User mapRow(ResultSet resultSet, int rowNumber) throws SQLException { User user = new User(); user.setId(resultSet.getInt("id")); user.setFirst_name(resultSet.getString("first_name")); user.setLast_name(resultSet.getString("last_name")); return user; } }); }
结果正确
##NamedParameterJdbcTemplate 在插入数据,能够经过?的形式插入参数。可是有不少列,每列都是?没法肯定意义。由此,Spring提供了NamedParameterJdbcTemplate,不在经过?形式定义参数,而是经过命名的形式
private NamedParameterJdbcTemplate namedParameterJdbcTemplate; @Autowired public void setDataSource(DataSource dataSource){ //this.jdbcTemplate = new JdbcTemplate(dataSource); this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); } public int countOfUsersByFirstName(String firstName){ String sql = "SELECT COUNT(*) FROM user WHERE first_name = :firstName;"; Map<String ,String> nameParameters = Collections.singletonMap("firstName",firstName); return this.namedParameterJdbcTemplate.queryForObject(sql,nameParameters,Integer.class); }
运行成功
##NamedParameterJdbcTemplate的接口形式
querForObject(String sql,Map<String,?> paramMap,RowMapper<T> rowMapper);
querForObject(String sql,SqlParameterSource paramSource,RowMapper<T> rowMapper);
##SqlParameterSource 在Spring就有简单的实现,最简单是MapSqlParameterSource,使用上和以前是相似的。还提供了另一种比较方便的形式BeanPropertySqlParameterSource,若是是对象的话,咱们使用map就比较麻烦,而使用BeanPropertySqlParameterSource就简单的多。这些Spring对SqlParameterSource的实现都是在org.springframework.jdbc.core.namedparam
包里面。
##BeanPropertySqlParameterSource的使用 使用BeanPropertySqlParameterSource,则须要有Entity对象类 在使用时,咱们须要直接传入对象,而不须要传入Map。
public int countOfUsersByFirstName(User user){ String sql = "SELECT COUNT(*) FROM user WHERE first_name = :first_name;"; SqlParameterSource sqlParameterSource = new BeanPropertySqlParameterSource(user); return this.namedParameterJdbcTemplate.queryForObject(sql,sqlParameterSource,Integer.class); }
**注意:**这里的User的属性,必须和所填入注入的SQL参数的名称保持一致。这里的SQL查询和上一个的查询SQL并不一致。
##异常处理 SQL的异常时checked的异常,要么捕获异常,要么在接口抛出异常。可是没法链接、语法错误、表列不存在,都会影响应用的正常使用,不是正常状态。
Spring是把SQLException转换到了DataAccessException,DataAccessException是unchecked的异常。写代码时不会try-catch。不用再代码中散布异常捕获。在业务处理的时候,在最终要返回业务的节点作处理。若是不能回复则给用户进行返回
##DataAccessException 在Spring中DataAccessException是基类,有不少异常处理都是基于DataAccessException。咱们能够根据子类的不一样名称判断错误类型。