零基础入门Java之JDBC(学习笔记)
学习地址
https://www.bilibili.com/video/av63345438
JDBC概念
JDBC(Java DataBase Connectivity,Java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。
JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去分别实现这套接口,提供数据库驱动 jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动 jar包中的实现类。
JDBC执行步骤
1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
—-1.复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下
—-2.右键–>Add As Library
2. 注册驱动
3. 获取数据库连接对象 Connection
4. 定义sql
5. 获取执行sql语句的对象 Statement
6. 执行sql,接受返回结果
7. 处理结果
8. 释放资源
使用eclipse创建javaweb使用jdbc,参考以下
https://blog.csdn.net/alexshi5/article/details/78956378
<% // ①注册数据库驱动 Class.forName("com.mysql.jdbc.Driver"); //String url1 = "jdbc:mysql://localhost:3306/(你的数据库名)"; String url1 = "jdbc:mysql://localhost:3306/tubu_dangqian"; String url2 = "?user=root&password=root"; String url3 = "&useUnicode=true&characterEncoding=UTF-8"; String url = url1 + url2 + url3; // ②获取数据库链接 Connection con = DriverManager.getConnection(url); // ③的定义sql语句 String sql = "Insert into tb_sms (event,mobile) values(?,?)"; // ④获取执行sql语句的对象 Statement PreparedStatement pstmt = con.prepareStatement(sql); pstmt.setString(1,"register"); pstmt.setLong(2,17625353140L); // ⑤执行sql,接受返回结果 int n = pstmt.executeUpdate(); if(n==1){%> 数据插入操作成功!<br> <% } else{%> 数据插入操作失败!<br> <% } if(pstmt!=null){pstmt.close();} // ⑥释放资源 if(con!=null){con.close();} %> |
详解各个对象
1.DriverManager:驱动管理对象
功能:
①注册驱动:告诉程序该使用哪一个数据库驱动(mysql5之后的jar包可以省略注册驱动)
static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } |
②获取数据库链接
数据库链接jdbc:mysql://localhost:3306/tubu_dangqian?user=root&password=root&useUnicode=true&characterEncoding=UTF-8
本地数据库链接可以省略数据库地址和端口:jdbc:mysql:///tubu_dangqian?user=root&password=root&useUnicode=true&characterEncoding=UTF-8
2.Connection对象
1.功能:
1.获取执行sql的对象
*Statement createStatement()
*PreparedStatement prepareStatement(String sql)
2.管理事务
开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为 false,即开启事务
提交事务:commit()
回滚:rollback()
3.Statement:执行sql的对象
1.执行sql
- ①.boolean execute(String sql):可以执行任意的 sql(了解)
- ②.int executeUpdate(String sql):执行DML(insert、update、delete)语句、DDL(create、alter、drop)语句(DDL语句不返回影响的行数)
返回值:影响的行数,可以通过这个影响的行数判读DML语句是否执行成功,如果返回值 > 0,则执行成功,反之,失败。 - ③.ResultSet executeQuery(String sql):执行DQL(select)语句
package com.jdbc; import java.sql.*; public class updateUser { public static void toDo() { Statement stmt = null; Connection con = null; try { // ① 注册驱动 Class.forName("com.mysql.jdbc.Driver"); // ② 获取数据库Connection对象 con = DriverManager.getConnection("jdbc:mysql://localhost:3306/tubu_dangqian?user=root&password=root"); // ③ 定义sql语句 //添加 String sql = "insert into tb_sms values(null,'register','17625353140')"; //更新 String sql = "update tb_sms set event = 'login111' where id = 11096"; String sql = "delete from tb_sms where id = 11096"; // ④ 获取执行sql的对象 Statement stmt = con.createStatement(); // ⑤ 执行sql int count = stmt.executeUpdate(sql); // ⑥ 处理结果 if(count>0) { System.out.println("操作成功"); }else { System.out.println("操作失败"); } } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); }finally { // ⑦ 释放资源 // 避免空指针异常 if(stmt!=null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(con!=null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } } } |
4.ResultSet:结果集对象,封装查询结果
1.基本使用
boolean next():游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回 false,否则返回 true
getXxx(参数):获取数据
Xxx:代表数据类型 如 int getInt(), String getString()
参数:
int:代表列的编号,从1开始 如 getString(1)
String:代表列的名称 如 getDouble(“balance”)
使用步骤:
游标向下移动一行
使用循环,用 next 方法判断是否有数据
获取数据
package com.jdbc; import java.sql.*; public class selectDemo { public static void toDo() { Connection con = null; Statement stmt = null; ResultSet result = null; try { Class.forName("com.mysql.jdbc.Driver"); con = DriverManager.getConnection("jdbc:mysql:///tubu_dangqian?user=root&password=root"); String sql = "select * from tb_sms"; stmt = con.createStatement(); result = stmt.executeQuery(sql); //处理结果 默认游标在最上边 while(result.next()) { System.out.println(result.getInt(1)); System.out.println(result.getString("event")); System.out.println(result.getLong(3)); } } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); }finally { // ⑦ 释放资源 // 避免空指针异常 if(result!=null) { try { result.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stmt!=null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(con!=null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } } } |
+++++++++++++++++++++++++++++++++
结果集封装成对象练习
User.java
package com.jdbc; public class User { private int id; private String username; public String toString() { return "id:"+this.getId()+"username:"+this.getUsername(); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } } |
UserDao.java
package com.jdbc; import java.util.ArrayList; import java.util.List; import com.jdbc.User; import java.sql.*; public class UserDao { public List<User> FindAll(){ List<User> list = new ArrayList<User>(); Connection con = null; Statement stmt = null; ResultSet result = null; try { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/user?user=root&password=root"; con = DriverManager.getConnection(url); String sql = "select * from users"; stmt = con.createStatement(); result = stmt.executeQuery(sql); while(result.next()) { int id = result.getInt(1); String userName = result.getString("user_name"); User user = new User(); user.setId(id); user.setUsername(userName); list.add(user); } } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); }finally{ if(result!=null) { try { result.close(); } catch (SQLException e1) { e1.printStackTrace(); } } if(stmt!=null) { try { stmt.close(); } catch (SQLException e1) { e1.printStackTrace(); } } if(con!=null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } return list; } public void toGo() { System.out.println(this.FindAll()); } } |
+++++++++++++++++++++++++++++++++
抽取JDBC工具类 : JDBCUtils
1. 注册驱动也抽取
2. 抽取一个方法获取连接对象
* 需求:不想传递参数(麻烦),还得保证工具类的通用性。
* 解决:配置文件
jdbc.properties
url=
user=
password=
3. 抽取一个方法释放资源
jdbc.properties文件
user=root password=root url=jdbc:mysql://localhost:3306/user driver=com.mysql.jdbc.Driver |
JDBCUtils.java
package utils; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.net.URL; import java.sql.*; import java.util.Properties; public class JDBCUtils { public static String driver; public static String url; public static String user; public static String password; /** * 文件的读取,只需要读取一次即可拿到这些值,使用静态代码块 */ static { try { Properties properties = new Properties(); // 获取src路径下的文件的方式 ClassLoader类加载器 ClassLoader classLoader = JDBCUtils.class.getClassLoader(); URL res = classLoader.getResource("jdbc.properties"); String path = res.getPath(); properties.load(new FileReader(path)); // 加载文件路径 properties.load(new // FileReader("C:\\Users\\Administrator\\eclipse-workspace\\javademo\\src\\jdbc.properties")); driver = properties.getProperty("driver"); user = properties.getProperty("user"); password = properties.getProperty("password"); url = properties.getProperty("url") + "?user=" + user + "&password=" + password; } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 获取数据库链接 */ public static Connection getConnnection() { Connection con = null; try { Class.forName(driver); con = DriverManager.getConnection(url); } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } return con; } /* * 释放资源 */ public static void closeResourse(Statement stmt, Connection connection) { if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } } |
UserDao2.java
package com.jdbc; import java.util.ArrayList; import java.util.List; import com.jdbc.User; import java.sql.*; import utils.JDBCUtils; public class UserDao2 { public List<User> FindAll(){ List<User> list = new ArrayList<User>(); Connection con = null; Statement stmt = null; ResultSet result = null; try { con = JDBCUtils.getConnnection(); String sql = "select * from users"; stmt = con.createStatement(); result = stmt.executeQuery(sql); while(result.next()) { int id = result.getInt(1); String userName = result.getString("user_name"); User user = new User(); user.setId(id); user.setUsername(userName); list.add(user); } } catch (SQLException e) { e.printStackTrace(); }finally{ if(result!=null) { try { result.close(); } catch (SQLException e1) { e1.printStackTrace(); } } JDBCUtils.closeResourse(stmt, con); } return list; } public void toGo() { System.out.println(this.FindAll()); } } |
5. PreparedStatement:执行sql的对象
1. SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
1. 输入用户随便,输入密码:a’ or ‘a’ = ‘a
2. sql:select * from user where username = ‘fhdsjkf’ and password = ‘a’ or ‘a’ = ‘a’
2. 解决sql注入问题:使用PreparedStatement对象来解决
3. 预编译的SQL:参数使用?作为占位符
4. 步骤:
1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
2. 注册驱动
3. 获取数据库连接对象 Connection
4. 定义sql
* 注意:sql的参数使用?作为占位符。 如:select * from user where username = ? and password = ?;
5. 获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(String sql)
6. 给?赋值:
方法: setXxx(参数1,参数2)
* 参数1:?的位置编号 从1 开始
* 参数2:?的值
7. 执行sql,接受返回结果,不需要传递sql语句
8. 处理结果
9. 释放资源
5. 注意:后期都会使用PreparedStatement来完成增删改查的所有操作
1. 可以防止SQL注入
2. 效率更高
++++++++++++++
JDBC控制事务:
1. 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
2. 操作:
1. 开启事务
2. 提交事务
3. 回滚事务
3. 使用Connection对象来管理事务
* 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
* 在执行sql之前开启事务
* 提交事务:commit()
* 当所有sql都执行完提交事务
* 回滚事务:rollback()
* 在catch中回滚事务
事务练习
package com.jdbc; import utils.JDBCUtils; import java.sql.*; public class jdbcDemo2 { public static void transfer() { Connection con = JDBCUtils.getConnnection(); PreparedStatement stmtMinus = null; PreparedStatement stmtPlus = null; try { con.setAutoCommit(false); String sql1 = "update users set balance = balance - 5 where id = ?"; stmtMinus = con.prepareStatement(sql1); String sql2 = "update users set balance = balance + 5 where id = ?"; stmtPlus = con.prepareStatement(sql2); stmtMinus.setInt(1,1); stmtPlus.setInt(1, 2); stmtMinus.execute(); stmtPlus.execute(); con.commit(); } catch (SQLException e) { System.out.print(e.getMessage()); try { if(con!=null) { con.rollback(); } } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); }finally { JDBCUtils.closeResourse(stmtMinus, con); JDBCUtils.closeResourse(stmtPlus, null); } } } |
数据库连接池
1.概念:
其实就是一个容器(集合),存放数据库连接的容器。当系统初始化好后,容器被创建,容器会申请有一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完,会将连接对象归还给容器。
2.好处:
节约资源
用户访问高效
3. 实现:
1. 标准接口:DataSource javax.sql包下的
1. 方法:
* 获取连接:getConnection()
* 归还连接:Connection.close()。如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了。而是归还连接
2. 一般我们不去实现它,有数据库厂商来实现
1. C3P0:数据库连接池技术
2. Druid:数据库连接池实现技术,由阿里巴巴提供的
Configuration files are normally looked up under standard names (c3p0.properties or c3p0-config.xml) at the top level of an application’s classpath, but the XML configuration can be placed anywhere in an application’s file system or classpath, if the system property com.mchange.v2.c3p0.cfg.xml is set.
4.C3P0
1 右键项目-Properties-Java Build Path-Libraries-Add External JARs-导入c3p0-0.9.5.2.jar和mchange-commons-java-0.2.11.jar
2 导入c3p0-config.xml
注意:通常情况下,java web项目会自动将WEB-INF项目下的lib添加到类路径下的,tomcat发布的部署包时也是会从lib目录下加载的。
但是开发阶段是会出现Eclipse没有自动将lib下的jar添加到类路径下,这是需要手动build path添加到类路径下的。
具体原因不明,但是作为一种经验可以记录下,如果发布时找不到加载类,就手动把lib下jar添加到build path下面。可以肯定的是项目部署到tomcat里面是不会出现这种情况的,只是在Eclipse开发环境中会出现。
使用连接池示例
package com.jdbc; import com.mchange.v2.c3p0.ComboPooledDataSource; import javax.sql.DataSource; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class C3P0Demo { public static void toGo() { DataSource ds = new ComboPooledDataSource("mysql"); Connection con = null; try { con = ds.getConnection(); String sql = "select * from users"; Statement stmt = con.createStatement(); ResultSet res = stmt.executeQuery(sql); while(res.next()) { System.out.println(res.getInt(1)+res.getString(2)); } } catch (SQLException e) { e.printStackTrace(); } System.out.println(con); } } |
src下的c3p0-config.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <named-config name="mysql"> <!-- 配置数据库用户名 --> <property name="user">root</property> <!-- 配置数据库密码 --> <property name="password">root</property> <!-- 配置数据库链接地址 --> <property name="jdbcUrl"><![CDATA[jdbc:mysql://127.0.0.1:3306/user?useUnicode=true&characterEncoding=UTF-8]]></property> <!-- 配置数据库驱动 --> <property name="driverClass">com.mysql.jdbc.Driver</property> <!-- 数据库连接池一次性向数据库要多少个连接对象 --> <property name="acquireIncrement">20</property> <!-- 初始化连接数 --> <property name="initialPoolSize">10</property> <!-- 最小连接数 --> <property name="minPoolSize">5</property> <!--连接池中保留的最大连接数。Default: 15 --> <property name="maxPoolSize">30</property> <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default:0 --> <property name="maxStatements">0</property> <!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 --> <property name="maxStatementsPerConnection">0</property> <!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能 通过多线程实现多个操作同时被执行。Default:3 --> <property name="numHelperThreads">3</property> <!--用户修改系统配置参数执行前最多等待300秒。Default: 300 --> <property name="propertyCycle">3</property> <!-- 获取连接超时设置 默认是一直等待单位毫秒 --> <property name="checkoutTimeout">1000</property> <!--每多少秒检查所有连接池中的空闲连接。Default: 0 --> <property name="idleConnectionTestPeriod">3</property> <!--最大空闲时间,多少秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 --> <property name="maxIdleTime">10</property> <!--配置连接的生存时间,超过这个时间的连接将由连接池自动断开丢弃掉。当然正在使用的连接不会马上断开,而是等待它close再断开。配置为0的时候则不会对连接的生存时间进行限制。 --> <property name="maxIdleTimeExcessConnections">5</property> <!--两次连接中间隔时间,单位毫秒。Default: 1000 --> <property name="acquireRetryDelay">1000</property> <!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试使用。Default: null --> <property name="automaticTestTable">Test</property> <!-- 获取connnection时测试是否有效 --> <property name="testConnectionOnCheckin">true</property> </named-config> </c3p0-config> |
5.Druid:Alibaba提供的数据库连接池技术
使用步骤:
导入jar包:
定义配置文件:
是Properties格式的
可以为任意的名称,放在任意的目录下
加载配置文件 Properties文件
获取连接池对象:使用工厂类获取DruidDataSourceFactory.createDataSource(pro);
获取连接,连接池对象.getConnection();
++++++++++++++++++++++
druid.properties文件
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/user username=root password=root initialSize=5 maxActive=10 maxWait=3000 |
package com.jdbc; import java.io.InputStream; import java.util.Properties; import javax.sql.DataSource; import java.sql.*; import com.alibaba.druid.pool.DruidDataSourceFactory; public class DruidDemo { public static void toDo() throws Exception { Properties pro = new Properties(); // 配置文件 InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties"); pro.load(is); DataSource ds = DruidDataSourceFactory.createDataSource(pro); Connection con = ds.getConnection(); System.out.println(con); } } |
JDBCUtils druid工具类
一般还是自己定义一个工具类,用来加载配置文件,初始化连接池,释放资源等
定义一个类JDBCUtils
在一个静态代码块里编写加载配置文件
方法:
获取连接的方法:通过数据库连接池获取连接
释放资源的方法
获取连接池的方法
+++++++++++++++++++++++++++++++++++++++
DRUIDJDBCUtils.java文件 封装数据库链接池工具类
package utils; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import javax.sql.DataSource; import com.alibaba.druid.pool.DruidDataSourceFactory; import com.jdbc.DruidDemo; public class DRUIDJDBCUtils { private static DataSource ds; /** * 获取链接 */ static { try { Properties pro = new Properties(); InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties"); pro.load(is); ds = DruidDataSourceFactory.createDataSource(pro); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 获取数据库链接资源 */ public static Connection getConnection() throws SQLException { return ds.getConnection(); } /** * 释放资源 */ public static void close(ResultSet res,Statement stmt, Connection con) { if(res!=null) { try { res.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (con != null) { try { con.close(); // 归还链接资源 } catch (SQLException e) { e.printStackTrace(); } } } } |
使用工具类
package com.jdbc; import java.sql.*; import utils.DRUIDJDBCUtils; public class jdbcDemo3 { public static void toGo() { Statement stmt = null; Connection con = null; ResultSet res = null; try { con = DRUIDJDBCUtils.getConnection(); String sql = "select * from users"; stmt = con.createStatement(); res = stmt.executeQuery(sql); while (res.next()) { System.out.println(res.getInt(1)); System.out.println(res.getString(2)); } } catch (SQLException e) { e.printStackTrace(); }finally { DRUIDJDBCUtils.close(res,stmt, con); } } } |
+++++++++++++++++++++++++++++++++++++++