JDBC介绍
从这篇文章开始,咱们将会介绍SpringBoot另外一个外围的技术,即数据库拜访技术,提到数据拜访,学习Java的同学霎时能就想起JDBC技术,JDBC 是 Java Database Connectivity 的全称,是Java语言中用来标准客户端程序如何来拜访数据库的利用程序接口,提供了诸如查问和更新数据库中数据的一套规范的API,这套规范不同的数据库厂家之间独特准守,并提供各自的具体实现。如图所示: http://img.mukewang.com/5ffeb...
这样设计的益处,就是Java程序只须要和JDBC API交互,从而屏蔽了拜访数据库的简单的实现,大大降低了Java程序拜访数据库的复杂度。对于日常开发而言,咱们只须要把握JDBC API 标准中的几个外围编程对象即可,这些对象包含DriverManger、Connection、Statement及ResultSet。
DriverManager
DriverManager次要负责加载不同数据库厂家提供的驱动程序包(Driver),并且依据不同的申请向Java程序返回数据库连贯(Connection)对象,先看下Driver接口的定义:
public interface Driver {
//获取数据库连贯 Connection connect(String url, java.util.Properties info) throws SQLException;boolean acceptsURL(String url) throws SQLException;DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) throws SQLException;int getMajorVersion();int getMinorVersion();boolean jdbcCompliant();public Logger getParentLogger() throws SQLFeatureNotSupportedException;
}复制代码
Driver中有个重要的办法 connect,来提供Connection对象
不同的数据库对Driver,有具体的实现,以MySql为例:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// 通过 DriverManager 注册 Driver static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); }}…
}复制代码
这里用到了DriverManager,DriverManager通过 registerDriver来注册不同数据库的Driver,并且还提供了getConnection返回数据库连贯对象。
Connection
通过DriverManager能够获取Connetion对象,Connection对象能够了解与数据库连贯的一种会话(Session),一个Connection对象代表一个数据库的连贯,负责实现与数据库底层的通信。
Connection对象提供了一组重载的办法来创立Statement和PreparedStatement,Statement和PreparedStatement是SQL执行的载体,另外Connection对象还会波及事务相干的操作。
Connection对象最外围的几个办法如下:
public interface Connection extends Wrapper, AutoCloseable { //创立 Statement
Statement createStatement() throws SQLException; //创立 PreparedStatementPreparedStatement prepareStatement(String sql) throws SQLException; //提交void commit() throws SQLException; //回滚void rollback() throws SQLException; //敞开连贯void close() throws SQLException;
}复制代码
Statement/PreparedStatement
Statement和PreparedStatement是由Connection对象来创立的,用来执行动态的SQL语句并且返回生成的后果集对象,这里存在两种类型,一种是一般的Statement,另外一种反对预编译的PreparedStatement。
所谓预编译,是指数据库的编译器会对 SQL 语句提前编译,而后将预编译的后果缓存到数据库中,下次执行时就能够通过替换参数并间接应用编译过的语句,从而大大提高 SQL 的执行效率。
以Statement为例,看下Statement最外围的办法:
public interface Statement extends Wrapper, AutoCloseable { //执行查问语句
ResultSet executeQuery(String sql) throws SQLException; //执行更新语句int executeUpdate(String sql) throws SQLException; //执行 SQL 语句boolean execute(String sql) throws SQLException; //执行批处理 int[] executeBatch() throws SQLException;
}复制代码
ResultSet
通过Statement或PreparedStatement执行SQL语句,咱们引出了另外一个对象即为ResultSet对象,代码如下:
public interface ResultSet extends Wrapper, AutoCloseable { //获取下一个后果
boolean next() throws SQLException; //获取某一个类型的后果值Value getXXX(int columnIndex) throws SQLException;…
}复制代码
ResultSet对象提供了next()办法,用来对整个后果集遍历操作,如果next()办法返回为true,阐明还有下一条记录,
咱们能够调用 ResultSet 对象的一系列 getXXX() 办法来获得对应的后果值。
JDBC拜访数据库流程
对于开发人员而言,通过JDBC的API是Java拜访数据库的次要路径,上面用代码来展现下拜访数据库的一个整体流程:
String url = "jdbc:mysql://localhost:3306/test" ;
String username = "root" ;
String password = "root" ;//1.通过DriverManager获取connection连贯Connection connection = DriverManager.getConnection(url,username,password);//2.创立preparedStatementPreparedStatement preparedStatement = connection.prepareStatement("select * from user");//3.执行SQL返回ResultSetResultSet resultSet = preparedStatement.executeQuery();//4.遍历resultSet后果集while (resultSet.next()){ //resultSet.getString("1");}//5.开释资源resultSet.close();
preparedStatement.close();
connection.close();复制代码
配置数据源
下面咱们在介绍JDBC的时候,Connection对象是通过DriverManager获取,Connection对象代表着和数据库的连贯,每次通过DriverManager获取比拟耗时,影响了零碎的性能。那有没有方法可能复用Connection对象呢,答案是必定的
JDBC给咱们提供了DataSource接口来实现Connection的复用,外围代码如下:
public interface DataSource extends CommonDataSource, Wrapper {
Connection getConnection() throws SQLException;
Connection getConnection(String username, String password) throws SQLException;
}复制代码
作为一种根底组件,不须要开发人员本人实现 DataSource,因为业界曾经存在了很多优良的实现计划,如 DBCP、C3P0 、Druid 、Hikari等
SpringBoot默认HikariDataSource作为DataSource的实现,当初咱们SpringBoot为例,看下SpringBoot如何通过JDBC来操作数据库的,在进行数据库操作之前,咱们首先须要先配置DataSource,SpringBoot配置DataSource非常简单,只须要在配置文件中增加DataSource的配置:
spring:
# datasource 数据源配置内容
datasource:
url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: root复制代码
应用JDBC操纵数据库
DataSource配好后,咱们在本地的数据库服务中,创立一个test数据库,并且执行以下DDL创立user表
CREATE TABLE user
( id
int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', username
varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户名', password
varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '明码', create_time
datetime DEFAULT NULL COMMENT '创立工夫',
PRIMARY KEY (id
), UNIQUE KEY idx_username
(username
)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;复制代码
接下来咱们创立一个实体类,实体类中属性和user表中字段一一对应
@Datapublic class User { /**
* 主键 */private Integer id; /** * 用户名 */private String username; /** * 明码 */private String password; /** * 创立工夫 */private Date createTime;
}复制代码
留神:这里应用了Lombok的@Data注解来生成get/set办法。
咱们再定义一个UserDao接口,
public interface UserDao { /**
* 新增 * @param user * @return */Integer insert(User user); /** * 依据ID查问 * @param id * @return */User selectById(Integer id); /** * 依据ID更新 * @param user * @return */Integer updateById(User user); /** * 依据ID删除 * @param id * @return */Integer deleteById(Integer id);
}复制代码
这里之所以要抽离出一个UserDao一层有两个起因:第一UserDao只封装了对use表的数据库操作,代码易于保护和治理,第二咱们能够基于UserDao接口提供不同的实现来拜访数据库,比方咱们能够提供基于原生JDBC的实现,也能够用JDBCTemplate实现数据库的拜访,还能够通过Mybatis等
接下来将通过代码模式来展现下SpringBoot是如何通过JDBC API对数据库进行CRUD操作的。咱们来定义UserDao的具体实现类命名为:UserRawJdbcDao实现以下办法:
新增数据
@Override
public Integer insert(User user) { final String SQL_INSERT = "INSERT INTO user(username, password, create_time) VALUES(?, ?, ?)"; Connection connection = null; PreparedStatement statement = null; ResultSet rs = null; Integer count = 0; try{ connection = dataSource.getConnection(); statement = connection.prepareStatement(SQL_INSERT, Statement.RETURN_GENERATED_KEYS); statement.setString(1,user.getUsername()); statement.setString(2,user.getPassword()); statement.setTimestamp(3,new Timestamp(user.getCreateTime().getTime())); count = statement.executeUpdate(); rs = statement.getGeneratedKeys(); if(rs.next()){ user.setId(rs.getInt(1)); } }catch (SQLException e){ e.printStackTrace(); }finally { try { if(rs != null){ rs.close(); } if(statement != null){ statement.close(); } if(connection != null){ connection.close(); } } catch (SQLException e) { e.printStackTrace(); } } return count;}复制代码
查问数据
@Override
public User selectById(Integer id) { final String SQL_SELECT_ID = "SELECT id,username,password,create_time FROM user WHERE id = ?"; Connection connection = null; PreparedStatement statement = null; ResultSet rs = null; User user = null; try{ connection = dataSource.getConnection(); statement = connection.prepareStatement(SQL_SELECT_ID); statement.setInt(1, id); rs = statement.executeQuery(); if(rs.next()){ user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); user.setPassword(rs.getString("password")); user.setCreateTime(rs.getTimestamp("create_time")); } }catch (SQLException e){ e.printStackTrace(); }finally { try { if(rs != null){ rs.close(); } if(statement != null){ statement.close(); } if(connection != null){ connection.close(); } } catch (SQLException e) { e.printStackTrace(); } } return user;}复制代码
更新数据
@Override
public Integer updateById(User user) { final String SQL_UPDATE = "UPDATE user SET username = ?, password = ? WHERE id = ?"; Connection connection = null; PreparedStatement statement = null; ResultSet rs = null; Integer count = 0; try{ connection = dataSource.getConnection(); statement = connection.prepareStatement(SQL_UPDATE); statement.setString(1,user.getUsername()); statement.setString(2,user.getPassword()); statement.setInt(3,user.getId()); count = statement.executeUpdate(); }catch (SQLException e){ e.printStackTrace(); }finally { try { if(rs != null){ rs.close(); } if(statement != null){ statement.close(); } if(connection != null){ connection.close(); } } catch (SQLException e) { e.printStackTrace(); } } return count;}复制代码
删除数据
@Override
public Integer deleteById(Integer id) { final String SQL_DELETE = "DELETE FROM user WHERE id = ?"; Connection connection = null; PreparedStatement statement = null; ResultSet rs = null; Integer count = 0; try{ connection = dataSource.getConnection(); statement = connection.prepareStatement(SQL_DELETE); statement.setInt(1,id); count = statement.executeUpdate(); }catch (SQLException e){ e.printStackTrace(); }finally { try { if(rs != null){ rs.close(); } if(statement != null){ statement.close(); } if(connection != null){ connection.close(); } } catch (SQLException e) { e.printStackTrace(); } } return count;}复制代码
到此,SpringBoot通过调用原生的JDBC的API实现对user表的CRUD操作,这代码对有代码洁癖的同学几乎不能忍,有大量共性的代码,如创立Connection、Statement、ResultSet、资源的开释和异样的解决。这部分封装和优化SpringBoot曾经解决过了,SpringBoot提供了JdbcTemplate模板工具类实现数据拜访,它简化了JDBC API的应用办法。
应用JdbcTemplate操纵数据库
同UserRawJdbcDao,咱们再定义UserDao的另外一套实现类命名为:UserJdbcDao,这套实现类是通过JdbcTemplate实现对数据库的操作,实现接口定义的办法如下:
新增数据
@Overridepublic Integer insert(User user){
// 创立 KeyHolder 对象,设置返回的主键 IDKeyHolder keyHolder = new GeneratedKeyHolder(); int count = jdbcTemplate.update(INSERT_PREPARED_STATEMENT_CREATOR_FACTORY.newPreparedStatementCreator( Arrays.asList(user.getUsername(),user.getPassword(),user.getCreateTime())),keyHolder); // 设置 ID 主键到 entity 实体中if (keyHolder.getKey() != null) { user.setId(keyHolder.getKey().intValue());} // 返回影响行数return count;
}复制代码
查问数据
@Override
public User selectById(Integer id){ User result = jdbcTemplate.queryForObject("SELECT id, username, password, create_time FROM user WHERE id=?", new BeanPropertyRowMapper<>(User.class), id); return result;}复制代码
更新数据
@Override
public Integer updateById(User user) { return jdbcTemplate.update("UPDATE user SET username = ?, password = ? WHERE id = ?", user.getUsername(),user.getPassword(),user.getId());}复制代码
删除数据
@Override
public Integer deleteById(Integer id){ return jdbcTemplate.update("DELETE FROM user WHERE id = ?", id);}复制代码
小结
通过比照咱们发现应用JdbcTemplate模板工具类能够大大减少JDBC拜访数据库的代码复杂度,作为开发人员咱们应该只关怀业务逻辑的具体实现过程,对JDBC底层对象的创立,资源的开释,异样的捕捉,应该交给框架对立保护和治理。
尽管JdbcTemplate缩小的咱们拜访数据库的代码量,不过应用也有一些问题,比方:新增数据的时候默认无奈返回生成主键的id,将SQL硬编码到Java代码中,如果SQL批改,须要从新编译Java代码,不利于零碎的保护等。这时咱们须要另外一个框架,它就是赫赫有名的Mybatis,下一篇我将会介绍SpringBoot如何整合Mybatis。
作者:彩色的灯塔