小菜鸟的自学“倒腾”之路(一)---回顾JDBC
小菜鸟的自学“倒腾”之路(一)
---回顾JDBC
1.初衷
本人目前是某211的大二在校生,热衷于“倒腾”关于Java Web。感觉接触到的东西越多,越觉得对于以前学过的知识的力不从心,刚好赶上五一假期,开了一个关于自己的小菜鸟博客来记录并总结以往学过的知识。今天,我们来谈谈JDBC。
2.什么是JDBC
JDBC全称为::Java Data Base Connectivity,它是可以执行SQL语句的Java API。
换句话说:JDBC其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
3.为什么要用到JDBC
- 市面上有非常多的数据库,本来我们是需要根据不同的数据库学习不同的API,sun公司为了简化这个操作,定义了JDBC API【接口】
- 对于我们来说,用Java操作数据库都是通过JDBC API【接口】,使用不同数据库,只要使用不同数据库厂商提高的操作程序即可
- 这大大简化了我们的学习成本,提高了学习效率
4.基本操作步骤
- 导入驱动jar包
- 注册驱动
- 获取数据库连接对象Connection
- 定义要执行的sql语句
- 获取执行sql语句的对象Statement
- 执行sql,接受返回的结果
- 处理结果
- 释放资源
5.代码实现
1、首先,创建一个lib目录,里面导入驱动资源jar包
2、
1 /** 2 2 * 示例:执行update操作 3 3 */ 4 4 public static void main(String[] args) { 5 5 String driver="com.mysql.cj.jdbc.Driver"; 6 6 String url="jdbc:mysql://127.0.0.1:3306/spj?&useSSL=false&serverTimezone=UTC"; 7 7 String user="root";//mysql用户名 8 8 String password="123456";//密码 9 9 Statement stat =null;//初始化 10 10 Connection conn =null;//初始化 11 11 try { 12 12 //注册驱动 13 13 Class.forName(driver); 14 14 //获取数据库连接对象 15 15 conn= DriverManager.getConnection(url, user, password); 16 16 //定义要执行的sql语句 17 17 String sql="update user set username = 'ycj' where id=1"; 18 18 //获取执行sql的对象 Statement 19 19 stat=conn.createStatement(); 20 20 int i = stat.executeUpdate(sql);//影响到的行数 21 21 System.out.println(i); 22 22 if(i>0) 23 23 System.out.println("sucessful"); 24 24 else 25 25 System.out.println("Fail"); 26 26 } catch (ClassNotFoundException e) { 27 27 e.printStackTrace(); 28 28 }catch (SQLException e) 29 29 { 30 30 e.printStackTrace(); 31 31 }finally { 32 32 if(stat!=null) { 33 33 try { 34 34 stat.close(); 35 35 } catch (SQLException e) { 36 36 e.printStackTrace(); 37 37 } 38 38 } 39 39 if(conn!=null) { 40 40 try { 41 41 conn.close(); 42 42 } catch (SQLException e) { 43 43 e.printStackTrace(); 44 44 } 45 45 } 46 46 } 47 47 }
6.注意问题
对于高版本的数据库(本人使用的时MySQL 8.XXX版本)需要强制SSL的使用,即使不使用,也需要在定义URL时显式的指出:useSSL=false ,而且需要指定时区:serverTimezone=UTC ,否则会抛出SQLException。
详解各个对象
1.DriverManager对象
DriverManager:驱动管理对象
*功能:
-
注册驱动:告诉程序该使用哪一个数据库驱动jar
static void registerDriver(Driver driver)注册与给定的驱动程序 DriverManager 。
写代码使用: Class.forName("com.mysql.cj.jdbc.Driver");
*需要注意的是:mysql 5之后的驱动包可以省略注册驱动步骤。* -
获取数据库连接:
* 方法:
static Connection getConnection(String url, String user, String password)* 参数:
* url:指定连接的路径
* 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
* 例子:jdbc:mysql://localhost:3306/spj
* 细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
* user:用户名
* password:密码
2.Connection对象
Connection:数据库连接对象,客户端与数据库所有的交互都是通过此对象完成。
功能:
1. 获取执行sql 的对象
Statement createStatement()
PreparedStatement prepareStatement(String sql)
2. 管理事务:
* 开启事务:
setAutoCommit(boolean autoCommit)
调用该方法设置参数为false,即开启事务
* 提交事务:
commit()
* 回滚事务:
rollback()
3.Statement对象
Statement:执行sql的对象
执行sql的方法:
1.
boolean execute(String sql)
可以执行任意的sql 了解
2.
int executeUpdate(String sql)
执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句
* 返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功
返回值>0的则执行成功,反之,则失败。
3.
ResultSet executeQuery(String sql)
执行DQL(select)语句
4.ResultSet对象
ResultSet:结果集对象,封装查询结果,当Statement对象执行executeQuery()时,会返回一个ResultSet对象。
方法:
* boolean next(): 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true
* getXxx(参数):获取数据
* Xxx:代表数据类型 如: int getInt() , String getString()
* 参数:
1. int:代表列的编号,从1开始 如: getString(1)
2. String:代表列名称。 如: getDouble("balance")
注意:
* 使用步骤:
1. 游标向下移动一行
2. 判断是否有数据
3. 获取数据
1 /** 2 *Result的使用 3 */ 4 public static void main(String[] args) { 5 String driver = "com.mysql.cj.jdbc.Driver"; 6 String url = "jdbc:mysql://127.0.0.1:3306/spj?&useSSL=false&serverTimezone=UTC"; 7 String user = "root"; 8 String password = "123456"; 9 Statement stat = null; 10 Connection conn = null; 11 ResultSet rs =null; 12 try { 13 Class.forName(driver); 14 conn = DriverManager.getConnection(url, user, password); 15 String sql = "select * from s"; 16 stat = conn.createStatement(); 17 rs = stat.executeQuery(sql); 18 19 //循环遍历,打印出表中的每一个数据 20 while(rs.next()) 21 { 22 String sno = rs.getString("sno"); 23 String sname = rs.getString("Sname"); 24 int status = rs.getInt("STATUS"); 25 System.out.println(sno+"---"+sname+"---"+status); 26 } 27 28 } catch (ClassNotFoundException | SQLException e) { 29 e.printStackTrace(); 30 } finally { 31 if (rs != null) { 32 try { 33 rs.close(); 34 } catch (SQLException e) { 35 e.printStackTrace(); 36 } 37 } 38 if (stat != null) { 39 try { 40 stat.close(); 41 } catch (SQLException e) { 42 e.printStackTrace(); 43 } 44 } 45 if (conn != null) { 46 try { 47 conn.close(); 48 } catch (SQLException e) { 49 e.printStackTrace(); 50 } 51 } 52 } 53 }
5.PreparedStatement对象
PreparedStatement:执行sql的对象,继承Statement对象,功能更强大,使用起来更简单。
-
Statement对象编译SQL语句时,如果SQL语句有变量,就需要使用分隔符来隔开,如果变量非常多,就会使SQL变得非常复杂。PreparedStatement可以使用占位符 ?来简化sql的编写
-
Statement会频繁编译SQL。
PreparedStatement可对SQL进行预编译,提高效率,预编译的SQL存储在PreparedStatement对象中
-
PreparedStatement可以防止sql注入问题。【Statement通过分隔符'++',编写永等式,可以不需要密码就进入数据库】
那我们如何使用PreparedStatement呢?
我们以一个令用户输入用户名和密码的案例来演示一下,数据库已经存储好了一些用户名和密码的键值对。
1 public boolean login(String username, String password) { 2 if (username == null || password == null) { 3 return false; 4 } 5 //连接数据库判断是否登录成功 6 Connection conn = null; 7 PreparedStatement stmt = null; 8 ResultSet rs = null; 9 10 try { 11 conn = Demo01JdbcUtils.getConnection();//使用工具类 12 String sql = "select * from user where username = ? and password =? "; 13 stmt = conn.prepareStatement(sql); 14 stmt.setString(1,username); 15 stmt.setString(2,password); 16 rs = stmt.executeQuery(); 17 return rs.next();//如果有下一行,返回true 18 19 } catch (SQLException e) { 20 e.printStackTrace(); 21 } finally { 22 Demo01JdbcUtils.close(rs, stmt, conn);//使用工具类 23 } 24 return false; 25 } 26
可以看到除了定义sql语句时使用了占位符,以及给占位符变量赋值以外,其他操作都是一样的。(工具类我们之后再说)
定义sql
\ * 注意:sql的参数使用?作为占位符。
如:select * from user where username = ? and password = ?;
给?赋值:
* 方法: setXxx(参数1,参数2) Xxx代表数据类型
* 参数1:?的位置编号 从1 开始
* 参数2:?的值
JDBC工具类
1.目的
我们了解了关于JDBC一些基础的实现,发现每次书写JDBC的代码时,总会有一些重复的步骤,非常的繁琐,所以为了方面我们书写代码,可以开发一个JDBC的实现类,实现相同的效果。
2.代码实现
1、首先我们想要获取连接对象,但是不想通过传递参数的方法,因为这样也很麻烦,为了保证工具类的通用性,我们使用配置文件的方式,配置一个jdbc.properties文件,里面放入一些固定的配置,以后只需要修改里面的数据库名称就好。
url = jdbc:mysql://127.0.0.1:3306/spj?&useSSL=false&serverTimezone=UTC user = root password = 123456 driver = com.mysql.cj.jdbc.Driver
2、
1 import java.io.FileReader; 2 import java.io.IOException; 3 import java.net.URL; 4 import java.sql.*; 5 import java.util.Properties; 6 7 /* 8 JDBC工具类 9 */ 10 public class Demo01JdbcUtils { 11 private static String url; 12 private static String password; 13 private static String user; 14 private static String driver; 15 16 /** 17 * 文件的读取,只需要读取一次即可拿到这些值,使用静态代码块。 18 */ 19 static { 20 try { 21 Properties pro=new Properties(); 22 //获取src路径下的文件方式: 使用ClassLoader 类加载器 23 24 ClassLoader classLoader = Demo01JdbcUtils.class.getClassLoader(); 25 URL res = classLoader.getResource("jdbc.properties"); 26 String path=res.getPath(); 27 System.out.println(path); 28 29 30 pro.load(new FileReader(path)); 31 url=pro.getProperty("url"); 32 user=pro.getProperty("user"); 33 password=pro.getProperty("password"); 34 driver=pro.getProperty("driver"); 35 36 Class.forName(driver); 37 } catch (IOException e) { 38 e.printStackTrace(); 39 } catch (ClassNotFoundException e) { 40 e.printStackTrace(); 41 } 42 } 43 44 /** 45 * 获取连接 46 * @return 连接对象 47 */ 48 public static Connection getConnection() throws SQLException { 49 return DriverManager.getConnection(url,user,password); 50 } 51 52 /** 53 * 释放资源 54 * @param stat 55 * @param conn 56 */ 57 public static void close(Statement stat,Connection conn) 58 { 59 if (stat != null) { 60 try { 61 stat.close(); 62 } catch (SQLException e) { 63 e.printStackTrace(); 64 } 65 } 66 if (conn != null) { 67 try { 68 conn.close(); 69 } catch (SQLException e) { 70 e.printStackTrace(); 71 } 72 } 73 } 74 public static void close(ResultSet rs,Statement stat,Connection conn) 75 { 76 if (stat != null) { 77 try { 78 stat.close(); 79 } catch (SQLException e) { 80 e.printStackTrace(); 81 } 82 } 83 if (conn != null) { 84 try { 85 conn.close(); 86 } catch (SQLException e) { 87 e.printStackTrace(); 88 } 89 } 90 if (rs != null) { 91 try { 92 rs.close(); 93 } catch (SQLException e) { 94 e.printStackTrace(); 95 } 96 } 97 } 98 99 }
JDBC对事务的操作
1.事务
1. 事务是作为单个逻辑工作单元执行的一系列操作。
2. 一个逻辑工作单元必须有四个属性,称为原子性、一致性、隔离性和持久性 (ACID) 属性,只有这样才能成为一个事务(要么同时成功,要么同时失败)
2.操作
2.1 使用Connection对象管理事务
Connection类中提供了3个事务处理方法:
• setAutoCommit(Boolean autoCommit):设置是否自动提交事务,默认为自动提交,即为true,通过设置false禁止自动提交事务,在执行sql之前使用此方法;
• commit():提交事务,当所有sql都执行完提交事务;
• rollback():回滚事务,在catch中回滚事务.
1 public static void main(String[] args) { 2 Connection conn = null; 3 4 PreparedStatement stmt1 = null; 5 PreparedStatement stmt2 = null; 6 ResultSet rs = null; 7 try { 8 conn= Demo01JdbcUtils.getConnection(); 9 //开启事务 10 conn.setAutoCommit(false); 11 12 String sql1="update s set STATUS = STATUS -? where sno=?"; 13 String sql2="update s set STATUS = STATUS +? where sno=?"; 14 stmt1=conn.prepareStatement(sql1); 15 stmt2=conn.prepareStatement(sql2); 16 stmt1.setInt(1,20); 17 stmt1.setString(2,"s1"); 18 stmt2.setInt(1,30); 19 stmt2.setString(2,"s2"); 20 stmt1.executeUpdate(); 21 //手动制造异常 22 int i=3/0; 23 stmt2.executeUpdate(); 24 25 conn.commit(); 26 27 } catch (Exception e) { 28 try { 29 if(conn!=null) 30 conn.rollback(); 31 } catch (SQLException throwables) { 32 throwables.printStackTrace(); 33 } 34 e.printStackTrace(); 35 }finally { 36 Demo01JdbcUtils.close(stmt1,conn); 37 Demo01JdbcUtils.close(stmt2,null); 38 } 39 }
2.2 事务的隔离级别
数据库定义了4个隔离级别:
- Serializable【可避免脏读,不可重复读,虚读】
- Repeatable read【可避免脏读,不可重复读】
- Read committed【可避免脏读】
- Read uncommitted【级别最低,什么都避免不了】
分别对应Connection类中的4个常量
- TRANSACTION*READ*UNCOMMITTED
- TRANSACTION*READ*COMMITTED
- TRANSACTION*REPEATABLE*READ
- TRANSACTION_SERIALIZABLE
数据库连接池
1.什么是数据库连接池
其实就是一个容器(集合),存放数据库连接的容器。
当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
与大二这学期学习的操作系统中线程池有着差不多的含义。
2.为什么要使用数据库连接池
• 数据库的连接的建立和关闭是非常消耗资源的
• 频繁地打开、关闭连接造成系统性能低下
3.C3P0
1、首先需要导入相应的jar包
2、定义配置文件:
* 名称: c3p0.properties 或者 c3p0-config.xml
* 路径:直接将文件放在根目录下即可。
- 创建核心对象 数据库连接池对象 ComboPooledDataSource
- 获取连接: getConnection
1 <?xml version="1.0" encoding="UTF-8"?> 2 <c3p0-config> 3 <!-- 默认连接池--> 4 <default-config> 5 <property name="driverClass">com.mysql.cj.jdbc.Driver</property> 6 <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/spj?useSSL=true&characterEncoding=utf8&serverTimezone=UTC</property> 7 <property name="user">root</property> 8 <property name="password">123456</property> 9 <property name="initialPoolSize">5</property> 10 <property name="maxPoolSize">20</property> 11 </default-config> 12 /* 13 默认连接池最大容量是20 14 */
1 public static void main(String[] args) throws SQLException { 2 3 DataSource ds=new ComboPooledDataSource();//使用默认配置 4 5 for(int i=1;i<=21;i++) 6 { 7 Connection coon=ds.getConnection(); 8 System.out.println(i+":"+coon); 9 //第五个使用完归还连接池 10 if(i==5) 11 coon.close(); 12 } 13 } 14
4.Druid
数据库连接池实现技术,由阿里巴巴提供的
步骤:
- 导入jar包 druid-1.0.9.jar
-
定义配置文件:
* 是properties形式的
* 可以叫任意名称,可以放在任意目录下
- 加载配置文件。Properties
- 获取数据库连接池对象:通过工厂类来获取 DruidDataSourceFactory
- 获取连接:getConnection
driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/spj?&useSSL=false&serverTimezone=UTC username=root password=123456 initialSize=5 maxActive=10 maxWait=3000
1 public static void main(String[] args) throws Exception { 2 3 //加载配置文件 4 Properties pro=new Properties(); 5 InputStream resourceAsStream = Demo01Druid.class.getClassLoader().getResourceAsStream("druid.properties"); 6 pro.load(resourceAsStream); 7 //获取连接池对象 8 DataSource ds = DruidDataSourceFactory.createDataSource(pro); 9 //获取连接 10 Connection conn=ds.getConnection(); 11 System.out.println(conn); 12 13 }
5.开发连接池工具类
1 import Demo02Druid.Demo01Druid; 2 2 import com.alibaba.druid.pool.DruidDataSourceFactory; 3 3 4 4 import javax.sql.DataSource; 5 5 import java.io.IOException; 6 6 import java.io.InputStream; 7 7 import java.sql.Connection; 8 8 import java.sql.ResultSet; 9 9 import java.sql.SQLException; 10 10 import java.sql.Statement; 11 11 import java.util.Properties; 12 12 13 13 /* 14 14 Druid连接池的工具类 15 15 16 16 */ 17 17 public class JDBCUtils { 18 18 //1、定义成员变量 19 19 private static DataSource ds; 20 20 static { 21 21 22 22 try { 23 23 //加载配置文件 24 24 Properties pro=new Properties(); 25 25 InputStream resourceAsStream = Demo01Druid.class.getClassLoader().getResourceAsStream("druid.properties"); 26 26 pro.load(resourceAsStream); 27 27 //获取连接池对象 28 28 ds = DruidDataSourceFactory.createDataSource(pro); 29 29 30 30 } catch (IOException e) { 31 31 e.printStackTrace(); 32 32 } catch (Exception e) { 33 33 e.printStackTrace(); 34 34 } 35 35 } 36 36 /** 37 37 * 获取连接 38 38 */ 39 39 public static Connection geConnection() throws SQLException { 40 40 return ds.getConnection(); 41 41 } 42 42 /** 43 43 * 释放资源 44 44 */ 45 45 public static void close(Statement stat,Connection conn) 46 46 { 47 47 if (stat != null) { 48 48 try { 49 49 stat.close(); 50 50 } catch (SQLException e) { 51 51 e.printStackTrace(); 52 52 } 53 53 } 54 54 if (conn != null) { 55 55 try { 56 56 conn.close(); 57 57 } catch (SQLException e) { 58 58 e.printStackTrace(); 59 59 } 60 60 } 61 61 } 62 62 public static void close(ResultSet rs, Statement stat, Connection conn) 63 63 { 64 64 if (stat != null) { 65 65 try { 66 66 stat.close(); 67 67 } catch (SQLException e) { 68 68 e.printStackTrace(); 69 69 } 70 70 } 71 71 if (conn != null) { 72 72 try { 73 73 conn.close(); 74 74 } catch (SQLException e) { 75 75 e.printStackTrace(); 76 76 } 77 77 } 78 78 if (rs != null) { 79 79 try { 80 80 rs.close(); 81 81 } catch (SQLException e) { 82 82 e.printStackTrace(); 83 83 } 84 84 } 85 85 } 86 86 /** 87 87 * 获取连接池方法 88 88 */ 89 89 public static DataSource getDataSource() 90 90 { 91 91 return ds; 92 92 }
6.JDBCTemplate
Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发
* 步骤:
- 导入jar包
- 创建JdbcTemplate对象。依赖于数据源DataSource
JdbcTemplate template = new JdbcTemplate(ds);
- 调用JdbcTemplate的方法来完成CRUD的操作
* update():执行DML语句。增、删、改语句
* queryForMap():查询结果将结果集封装为map集合,将列名作为key,将值作为value 将这条记录封装为一个map集合
* 注意:这个方法查询的结果集长度只能是1
* queryForList():查询结果将结果集封装为list集合
* 注意:将每一条记录封装为一个Map集合,再将Map集合装载到 List集合中
*query():查询结果,将结果封装为JavaBean对象
query的参数:RowMapper
* 一般我们使用BeanPropertyRowMapper实现类。可以完成数据到 JavaBean的自动封装
*new BeanPropertyRowMapper<类型>(类型.class)
* queryForObject:查询结果,将结果封装为对象
* 一般用于聚合函数的查询

浙公网安备 33010602011771号