JDBC笔记
一、JDBC介绍
概念:Java DataBase Connectivity`
Java
数据库连接,使用Java
语言操作数据库JDBC本质
Java官方(Sun)定义的一套操作所有关系型数据库的规则。各数据库厂商实现者套接口,提供数据库驱动jar包。程序员就可以使用这套接口(JDBC)编程,真正执行代码的是驱动jar包中的实现类【多态】
二、快速入门
步骤:
导入驱动jar包
注册驱动【让程序识别是哪家的数据库】
//注册驱动 Class.forName("com.mysql.jdbc.Driver"); // Driver 类中有一静态代码块,是对驱动的注册,保证了注册驱动的代码仅在类加载时执行唯一的一次 static { try{ DriverManager.registerDriver(new Driver()); } catch(SQLException E) { throw new RuntimeException("Can't register driver!");}}
获取数据库对象
Connection
使用
DriverManager
类的静态方法获取数据库的连接//获取数据库连接 Connection conn = DriverManager.getConnection("jdbc:mysql:///jdbctest", "root", "admin"); //参数: // user:数据库用户名 // password:数据库登录密码 // URL:指定连接的路径 // MySQL连接的URL:jdbc:mysql://IP地址(域名):端口号/数据库名称【如 "jdbc:mysql://127.0.0.1:3306/jdbctest" 】 // 省略写法:如果连接的是本地MySQL服务器,且端口为3306,可以省略域名与端口
定义SQL【将来把SQL发送给数据库】
//定义一条插入语句 String sql0 = "insert into test01(name) values('汪星人')"; //定义一条修改语句 String sql = "update test01 set name = '喵喵喵星人' where id = 4";
获取执行SQL语句的对象
Statement
使用
Connection
类的createStatement()
方法获取执行SQL语句的对象Statement sta = conn.createStatement();
执行SQL,接收返回结果
处理结果
- 如果是增删改语句,会返回数据库中表的受影响的行数
- 如果是查询语句,会返回一个查询到的结果集
释放资源
Connction
与Statement
对象时从底层申请的进程资源,用完需要释放
三、JDBC中各个类与接口详解
DriverManager
对象功能:
注册驱动【告诉程序应该使用哪个数据库驱动jar包】
//DriverManager类中注册驱动的方法 static void registerDriver(Driver driver, DriverAction da); //一般下面的方式注册 Driver类中静态代码块执行上述方法 //方法执行。Driver类进入内存,其静态代码块执行,驱动进行注册 Class.forName("com.mysql.jdbc.Driver");
2.获取数据库的连接
// 尝试建立与给定数据库URL的连接。 static Connection getConnection(String url, String user, String password);
Connection
对象功能 代表了当前代码与数据库之间的桥梁
获取SQL语句的执行对象
Statement createStatement() PreparedStatement prepareStatement(String sql)
管理事务
// 开启事务: 设置参数为false则为手动提交【为true是autoCommit 自动提交】 void setAutoCommit(boolean autoCommit) //回滚事务: 回滚并释放此 Connection对象当前持有的任何数据库锁。 void rollback() //提交事务: 提交,并释放此 Connection对象当前持有的任何数据库锁。 void commit()
Statement
对象功能:用于执行静态的SQL语句并返回其生成的结果的对象【该对象可能会产生SQL注入问题】
主要方法
【了解】 可以执行任意语句的方法
//返回值: true--第一个结果为ResultSet对象 false--更新操作或者无结果 boolean execute(String sql)
可以执行DML语句(增删改)与DDL语句【此条不常用】的方法
//执行DML语句(增删改表数据 INSERT, UPDATE, DELETE)返回值为影响的行数 // 执行DDL语句(增删改库、表 create、drop、alter) -- 不常用,无返回值0 int executeUpdate(String sql)
可以执行DQL语句(查询)的方法
// 执行DQL语句(select),该语句返回单个 ResultSet对象。 ResultSet executeQuery(String sql)
ResultSet
对象功能:结果集对象,封装了查询到的结果
ResultSet对象有一个游标,初始状态指向了查询的表的第一行上面一行
主要方法
//将光标从当前位置向前移动一行。如果新的当前行有效,返回值true; //如果没有更多的行 返回false boolean next() // next方法可以使对象的游标向下移动一行
//getxxx(参数): //根据获取到的结果的类型不同,方法名不同 int getInt(参数); //用于获取数据库中int类型的字段 String getString(参数) //用于获取数据库表中varchar类型的字段 // 两种形式参数 //2.1 int columnIndex 参数是当前行某一列的编号【从1开始,不是索引】 // 2.2 String columnLabel:参数是当前行某一列的列名【比如 id name sex...】
ResultSet对象操作步骤
- 游标向下移动一行
- 判断该行是否还有数据【next()的返回值】
- 获取数据
PreparedStatement
对象功能:用于执行预编译的SQL语句并返回其生成的结果的对象
解释:
预编译的SQL语句:参数使用 ? 作为占位符 所以使用PreparedStatement对象时需要给 ? 赋值
PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES SET SALARY = ? WHERE ID = ?");
使用步骤与
Statement
对象的不同之处:定义SQL语句时:
//SQL语句的参数使用 ? 作为占位符 比如 String sql = "select * from user_login where userName = ? and userPassWord = ?";
获取SQL语句执行对象时:
//与Statement对象获取有所不同 Statement对象获取: Statement stmt = conn.createStatement(); //PreparedStatement对象获取 需要传递一个参数 PreparedStatement pstmt = conn.prepareStatement(String sql);
执行SQL语句前:
//执行SQL前,需要给占位符进行赋值,组成完整的SQL语句 /* 赋值方法:void setXxx(int parameterIndex, byte x) 参数1:? 的位置编号【有俩 ? 】,从1开始 参数2: ? 的值*/ // 比如: String sql = "select * from user_login where userName = ? and userPassWord = ?"; //为占位符 ? 赋值 占位符代表的用户名与密码都是字符串,所以使用setString()方法赋值 pstmt.setString(1, userName); pstmt.setString(2, userPassword);
与
Statement
对象相比的好处- 可以防止SQL注入
- 效率更高
四、JdbcUtils工具类
概述: JDBC很多代码都是重复的,为了提高效率【懒】,定义一个工具类抽取一些重复度且相对固定的代码,简化书写
应该具有的方法:
- 驱动的注册
- 数据库连接的获取
需求:不想动DriverManager.getConnection()方法的参数,给它放在配置文件中,保证工具的通用性 - 资源的释放
- 数据库连接的获取
- 驱动的注册
实现:
public class JdbcUtils { //三个变量也设置为静态,让静态代码块与静态方法可以访问 private static String url; private static String user; private static String password; /* 期望文件的读取仅执行一次 把得到的url user 等信息给下面的getConnection方法参数使用,所以得提升三个变量的作用域 使用静态代码块可以让下面这段代码在类加载时执行唯一一次 */ static{ //静态代码块的形式获取配置文件的内容 Properties prop = new Properties(); //获取类加载器, ClassLoader,本类的类加载器可以找到src下的配置文件 ClassLoader classLoader = JdbcUtils.class.getClassLoader(); // 使用getResourceAsStream方法获取配置文件路径并以输入流的形式返回 InputStream propInputStream = classLoader.getResourceAsStream("login.properties"); /* 不以流的形式返回,以统一资源定位符的形式返回,再使用getPath方法转换绝对路径为字符串 URL res = classLoader.getResource("jdbc.properties"); String perprotiesPath = res.getPath();*/ //传递配置文件的流对象 try { //读取配置文件的信息【加载到内存】并保存到Properties集合中 prop.load(propInputStream); //把配置文件信息赋值给url user password 变量 url = prop.getProperty("url"); user = prop.getProperty("user"); password = prop.getProperty("password"); String driver = prop.getProperty("Driver"); //注册驱动 Class.forName(driver); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); }finally { //释放配置文件流对象资源 try { propInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } public static Connection getConnection() throws SQLException { //数据库的连接,不想在实际类与这里动代码,把用户的表信息等等放在配置信息中 return DriverManager.getConnection(url, user, password); } /** * 定义资源释放方法,参数为只有Statement Connection两个对象【针对DML语句,不查询】 * @param stmt 参数传递 Statement对象 * @param conn 参数传递 Connection对象 */ public static void close(Statement stmt, Connection conn) { if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } /** * 资源释放的重载方法 * 定义资源释放方法,参数为 ResultSet, Statement, Connection两个对象【针对DQL语句,查询】 * @param rs 参数传递 ResultSet对象 * @param stmt 参数传递 Statement对象 * @param conn 参数传递 Connection对象 */ public static void close(ResultSet rs, Statement stmt, Connection conn) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } close(stmt, conn); } }
JDBC很多代码都是重复的,为了提高效率【懒】,定义工具类 抽取出一些重复的代码。简化书写 定义一个JDBC工具类,里面定义静态方法进行
五、Jdbc事务管理
事务:一个包含多个步骤的业务操作 ,如果这个业务操作被事务管理,则这个多个步骤要么同时成功,要么同时失败
操作:
- 开启事务
- 提交事务
- 回滚事务
使用Jdbc的Connection对象来管理事务
- 开启事务:
setAutoCommit(boolean autoCommit)
:调用该方法设置参数为false,即开启事务【意即关闭自动提交】 - 提交事务:
commit()
- 回滚事务:
rollback()
- 操作时机:在执行操作前开启事务;在全部执行完后提交事务;如果有问题,在异常处理逻辑处
catch
里面进行回滚
- 开启事务:
六、数据库连接池
概念
- 每次执行都需要从系统获取Connection对象,用完还关闭,比较费时费力,效率低下
- 其实就是一个容器【集合】,存放数据库连接,当任务访问数据库时,从容器中获取连接对象,使用完毕后归还给容器,达到复用的效果
好处
- 节约资源
- 用户访问高效
实现
标准接口:javax.sql 包下的 interface DataSource
方法:获取连接
获取连接
Connection
getConnection()
尝试建立与此DataSource
对象所代表的数据源的连接。Connection
getConnection(String username, String password)
尝试建立与该DataSource
对象所代表的数据源的连接。归还连接:如果数据库连接对象Connection是由连接池获取的,则调用
Connection.close()
方法不是释放资源,是归还连接对象到连接池
一般不是我们自己去实现它,由数据库厂商取实现
- C3P0:数据库连接池技术
- Druid:数据库连接池技术【有阿里巴巴提供的】
C3P0:数据库连接池技术
4.1 导入jar包 :
- c3p0-0.9.2.1.jar
- mchange-commons-java-0.2.3.4.jar【依赖jar包。上面的jar里面的类依赖这个包】
- 数据库驱动jar包
4.2 使用方式
- 配置文件方式
- 定义配置文件
- 名称:c3p0.propertied 或者 c3p0-config.xml 【不能是其他名称】
- 路径:默认在项目类路径(classpath)下【直接将配置文件放在src目录下即可】
- 定义配置文件
- 硬编码,创建连接池、路径...,代码写死【后期维护麻烦,不推荐】
- 配置文件方式
4.3 创建核心对象 --- 数据库连接池对象 CombPooledDataSource
4.4 获取连接:getConnection()方法【都实现了DataSource接口】
4.5 实例
//1. 获取数据库连接池 DataSource ds = new ComboPooledDataSource(); //2. 获取数据库连接对象 Connection conn = null; PreparedStatement pstmt = null; try { conn = ds.getConnection(); System.out.println("conn --> " + conn); String sql = "insert into user_login values(null, ? , ?)"; pstmt = conn.prepareStatement(sql); //为通配符赋值 //为通配符 ? 赋值 pstmt.setString(1, "mmmm"); //为密码赋值 pstmt.setString(2, "mmmmmm"); //执行SQL int count = pstmt.executeUpdate(); System.out.println("受影响的行数:" + count); } catch (SQLException e) { e.printStackTrace(); }finally { //3. 归还连接 ...}
druid数据库连接池技术
5.1 导入jar包 druid-1.0.9.jar 与数据库驱动jar包
5.2 定义配置文件
- 是properties形式的
- 可以叫任意名称,可以放在任意目录
5.3 获取数据库连接池对象:通过工厂类获取的,不是new出来的
//找到配置文件 ClassLoader classLoader = Demo01Druid.class.getClassLoader(); //类加载器可以找到 druid.properties 文件,以输入流的形式返回 InputStream druidProp = classLoader.getResourceAsStream("druid.properties"); //创建 Properties 集合 Properties prop = new Properties(); //load读取druid.properties 配置文件 prop.load(druidProp); //3。 获取数据库连接池对象:通过工厂类获取的 DataSource ds = DruidDataSourceFactory.createDataSource(prop); //4. 获取数据库连接对象 Connection conn = ds.getConnection();
5.4 获取连接 getConnection()方法【实现了DataSource接口】
5.5 使用数据库连接池定义工具类JdbcUtils
1.定义一个类 JdbcUtils
2.提供静态代码块加载配置文件,初始化获取数据库的连接池【一个池子就够了】
3.工具类提供方法
- 获取数据库连接对象
- 释放资源
- 获取连接池的方法
具体实现:
//MyJdbcUtils.java private static DataSource dataSource = null; //获取数据库连接池的静态代码块 static { //获取类加载器,让它找到配置文件 ClassLoader classLoader = MyJdbcUtils.class.getClassLoader(); InputStream druidProp = classLoader.getResourceAsStream("druid.properties"); Properties prop = new Properties(); //把配置文件信息装载进入集合中 try { prop.load(druidProp); //使用工厂类获取数据库的连接池,参数是装载了配置文件信息的Properties集合 dataSource = DruidDataSourceFactory.createDataSource(prop); } catch (Exception e) { e.printStackTrace(); } finally { //释放资源 if (druidProp != null) { try { druidProp.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 获取数据库连接对象 * @return 返回一个数据库的连接对象 * @throws SQLException */ public static Connection getConnection() throws SQLException { //获取连接池中的数据库连接对象,并返回出去 return dataSource.getConnection(); } /** * 获取数据库连接池的方法 * @return */ public static DataSource getDataSource() { //返回数据库连接池 return dataSource; } /** * 归还数据库连接对象、释放资源 * @param rs * @param stmt * @param conn */ public static void close(ResultSet rs, Statement stmt, Connection conn) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } close(stmt, conn); } /** * close方法的重载形式 * @param stmt * @param conn */ public static void close(Statement stmt, Connection conn) { if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
七、 JDBC封装--Spring Jdbc:JdbcTemplate
概念:
- Spring 框架对JDBC的简单封装。提供了一个JdbcTemplate对象,简化了Jdbc的开发
使用步骤
导入包【5个需要的包与数据库驱动包】
创建
JdbcTemplate
对象,依赖于数据源DataSource
数据库连接池JdbcTemplate template = new JdbcTemplate(ds);
【DataSource
可以通过五 中的工具类获取】
调用JdbcTemplate方法完成CRUD操作
update():执行DML语句,对数据进行增删改,返回值为受影响的行数
Map<String, Object> queryForMap(String sql, Object... args):查询结果,将结果集封装为Map集合
- 注意!!这个方法查询的结果集长度为1,不能查询多行记录,将列名作为Key,列的值作为Value。封装为一个Map集合
List<Map<String, Object>> queryForList(String sql):查询结果,将结果集封装为List集合
- 表示把上一步获得的每一个Map集合【也就是独立的一条记录】作为一个元素
封装在集合中- Sting类型是列名
- Objects类型是各个列下的对应的值
- 比如:id=1007, ename=刘备, job_id=2
- 表示把上一步获得的每一个Map集合【也就是独立的一条记录】作为一个元素
<t> List<t> query(String sql, RowMapper<t> rowMapper):查询结果,将结果集封装为JavaBean对象</t></t></t>
方法第二个参数是一个
RowMapper<T>
接口的实现类,可以是自己定义的,也可以是别人定义好的使用别人定义好的:使用 BeanPropertyRowMapper实现类,构造方法传递要映射的Javabean类【比如Person】以及其字节码对象【比如Person.class】,可以自动找到列的属性与数据库字段名是否匹配
List<Person> list = template.query(sql, new BeanPropertyRowMapper<Person>(Person.class));
<t> T queryForObject(String sql, Class<t> requiredType):查询结果,将结果集封装为对象【一般是基本数据类型对象】</t></t>
- 第二个参数需要传递将来要封装的返回值的结果的类型 。比如int.class
- 这个方法一般是用来执行聚合函数的 比如
COUNT(id)