###JDBC
- 学习目的:通过java代码访问数据库
- 什么是JDBC: Java Database Connectivity java数据库连接,实际上JDBC就是java语言中一套和数据库交互的api
- 为什么使用JDBC:因为Java程序员工作时需要连接多种数据库(Mysql、oracle,db2等)为了避免每一个数据库都学习一套新的api,Sun公司定义了JDBC接口(方法的声明),各个数据库厂商去写实现类(驱动),这样Java程序员只需要掌握JDBC的调用 即可访问任何数据库。
(JDBC接口:一套统一标准的接口)
####如何使用JDBC
- 从maven私服中搜索mysql 找到 5.1.6版本的jar坐标 复制到自己的pom文件中。
登陆达内的私服:maven.tedu.cn
登陆阿里私服:maven.aliyun.com
truncate table jdbc01;//删除表并且重建表
具体操作如下:
1. 创建maven工程
2. 下载mysql相关jar包
- 首页搜索栏输入mysql 搜索
- 找到5.1.6版本 复制坐标 到自己工程的pom.xml文件里
- 实现代码如下:
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接对象
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/db3",
"root", "root");
//3.创建sql执行对象
Statement stat = conn.createStatement();
//4. 执行sql
String sql =
"create table if not exists jdbct1(id int,name varchar(10))";
stat.execute(sql);
System.out.println("执行完成!");
//5. 关闭资源
stat.close();
conn.close();
###JUnit 单元测试
- 可以在一个类中添加多个可执行的方法
- 在无参无返回值的方法上面添加 @Test 右键 run as
package cn.tedu; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import org.junit.Test; public class Demo02 { //JUnit单元测试: 在需要运行的方法上面添加@Test注解, //要求方法必须是无参无返回值。 @Test public void test01(){ System.out.println("方法一执行"); } @Test public void test02(){ System.out.println("方法二执行"); } @Test public void insert() throws Exception{ Class.forName("com.mysql.jdbc.Driver");//注册驱动 Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/db3", "root", "root");//获取连接 Statement stat = conn.createStatement();//执行sql对象 String sql = "insert into jdbc01 values(null,'Tom',18)"; stat.executeUpdate(sql); System.out.println("新增完成"); //关闭资源 stat.close(); conn.close(); } @Test public void delete() throws Exception{ Class.forName("com.mysql.jdbc.Driver");//注册驱动 Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/db3", "root", "root");//获取连接 Statement stat = conn.createStatement();//执行sql对象 String sql = "delete from jdbc01 where id=1"; stat.executeUpdate(sql); System.out.println("删除完成"); //关闭资源 stat.close(); conn.close(); } @Test public void update() throws Exception{ Class.forName("com.mysql.jdbc.Driver");//注册驱动 Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/db3", "root", "root");//获取连接 Statement stat = conn.createStatement();//执行sql对象 String sql = "update jdbc01 set age=200 where id=2"; stat.executeUpdate(sql); System.out.println("修改完成"); //关闭资源 stat.close(); conn.close(); } @Test public void findAll() throws Exception{ Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/db3", "root", "root"); Statement stat = conn.createStatement(); //执行查询 ResultSet rs = stat.executeQuery("select * from jdbc01"); while(rs.next()){ //int id = rs.getInt("id"); String name = rs.getString("name"); System.out.println("name:"+name); } //关闭资源 rs.close(); stat.close(); conn.close(); } //查询emp表中所用员工的姓名+工资 控制台输出 //小明每月工资3000元 package cn.tedu; /*把死的写活*/ import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class DBUtils2 { private static String driver; private static String url; private static String username; private static String password;//全局变量 方便调用 static { //创建属性对象 Properties prop = new Properties(); //得到文件的输入流 InputStream ips = DBUtils2.class.getClassLoader() .getResourceAsStream("jdbc.properties"); //把文件加载到属性对象中 try { prop.load(ips); //读取数据 driver = prop.getProperty("driver"); url = prop.getProperty("url"); username = prop.getProperty("username"); } catch (IOException e) { e.printStackTrace(); } }//static提高执行效率 //1. 获取连接 public static Connection getConn() throws Exception{ //注册驱动 Class.forName(driver); //获取连接对象 Connection conn = DriverManager.getConnection( url,username,password); return conn; } //2. 关闭资源 public static void close(ResultSet rs, Statement stat,Connection conn){ //关闭resultset try { if(rs!=null){ rs.close(); } } catch (SQLException e) { e.printStackTrace(); } //关闭stat try { if(stat!=null){ stat.close(); } } catch (SQLException e) { e.printStackTrace(); } //关闭连接 try { if(conn!=null){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } }
####Statement的相关函数
- execute: 可以执行任意sql 但是推荐执行DDL(什么是DDL?这个也比较重要看看前面的笔记)
返回值代表的是 是否有结果集(了解)
- executeUpdate: 推荐执行DML (增删改)
返回值代表的生效行数
- executeQuery:执行DQL
返回值是ResultSet
###测试插入,修改,删除数据 代码参见 Demo02.java
###ResultSet
- 此对象中保存着从数据库查询回的数据
- next(): 判断是否有下一条数据有则返回true,并且内部的游标向下移动一格
- 从对象中获取数据的两种方式:
1. String name = rs.getString("name");
2. String name = rs.getString(2); //从1开始
###数据库类型和java类型对比
mysql数据库 java
int getInt
varchar getString
float/double getFloat/getDouble
datetime/timestamp getDate
###解决jdbc乱码问题
在url后面添加 (从?开始)
jdbc:mysql://localhost:3306/db3?useUnicode=true&characterEncoding=UTF-8
###数据库连接池 DBCP(http://maven.tedu.cn/ ----> dbcp)
- DataBase Connection Pool: 数据库连接池
- 为什么使用: 如果没有连接池,每一次业务都需要和数据库服务器建立一次连接,业务处理完还要断开连接,如果有上万次业务请求则会有上万次的开关连接,频繁开关连接非常浪费资源,使用数据库连接池可以重复使用连接池中的连接,从而提高网站的响应能力。
(在处理业务时,多次频繁地连接和断开,容易降低效率,而使用数据库连接池中的连接,可以大大提高网站的响应能力)
- 如何使用:
1. 下载相关jar包: 从maven.tedu.cn中搜索dbcp
2. 关键代码:
//创建数据源对象 BasicDataSource dataSource = new BasicDataSource(); //设置数据库连接信息 dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/db3"); dataSource.setUsername("root"); dataSource.setPassword("root"); //设置连接池参数 dataSource.setInitialSize(3);//初始连接数量 dataSource.setMaxActive(5);//最大连接数量 //获取连接池中的连接 Connection conn = dataSource.getConnection(); System.out.println(conn);
package cn.tedu; /*数据库连接池的使用*/ import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import org.apache.commons.dbcp.BasicDataSource; public class DBUtils3 { private static String driver; private static String url; private static String username; private static String password;// 全局变量 方便调用 private static BasicDataSource dataSource; static { // 创建属性对象 Properties prop = new Properties(); // 得到文件的输入流 InputStream ips = DBUtils3.class.getClassLoader().getResourceAsStream("jdbc.properties"); // 把文件加载到属性对象中 try { prop.load(ips); // 读取数据 driver = prop.getProperty("driver"); url = prop.getProperty("url"); username = prop.getProperty("username"); // 创建基础数据源 BasicDataSource dataSource = new BasicDataSource(); // 设置数据库连接信息 dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); // 设置连接池参数 dataSource.setInitialSize(3);// 初始化数据库连接数量 dataSource.setMaxActive(5);// 最大连接数量 } catch (IOException e) { e.printStackTrace(); } }// static提高执行效率 // 1. 获取连接 public static Connection getConn() throws Exception { Connection conn = dataSource.getConnection(); System.out.println(conn); return conn; } // 2. 关闭资源 public static void close(ResultSet rs, Statement stat, Connection conn) { // 关闭resultset try { if (rs != null) { rs.close(); } } catch (SQLException e) { e.printStackTrace(); } // 关闭stat try { if (stat != null) { stat.close(); } } catch (SQLException e) { e.printStackTrace(); } // 关闭连接 try { if (conn != null) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } }
PreparedStatement
- 预编译的Sql执行对象
- 好处:
- 代码可读性更高,不容易出错(因为statement拼接字符串又麻烦又易出错)
- 带有预编译效果,执行效率比statement略高
-
可以避免sql注入, 因为预编译的时候已经sql语句的逻辑固定,替换?进去的内容只能以值的形式体现,如果包含逻辑sql内容 则无效
-
sql中有变量就用preparedStatement 没有变量就用statement
-
使用方式:
- 关键步骤:
-
//创建预编译sql执行对象 String sql = "insert into user values(null,?,?)"; stat = conn.prepareStatement(sql); //把?替换成真正的值 stat.setString(1, name); stat.setInt(2, age); //执行sql stat.executeUpdate();//返回执行有效的行数
通过登陆案例演示sql注入
create table tlogin (id int primary key autoincrement,username varchar(10),password varchar(10)); insert into t_login values(null,'libai','admin'),(null,'lisi','admin');
select count(*) from t_login where username='sdfsd' and password='' or '1'='1';
注入代码 ' or '1'='1
批量操作 batch
-
批量操作:可以把多次sql合并成一次执行,避免了频繁和数据库服务器进行交互,从而提高执行效率
-
代码参见:TestBatch.java
批量插入避免内存溢出
//创建sql执行对象 String sql = "insert into user values" + "(null,?,?)"; stat = conn.prepareStatement(sql); for(int i=0;i<100;i++){ stat.setString(1, "name"+i); stat.setInt(2, i); //添加到批量处理 stat.addBatch(); if (i%20==0) { stat.executeBatch(); //清空执行过的sql 不然会重复添加 stat.clearBatch(); } } //执行sql stat.executeBatch(); System.out.println("执行完成!");
分页查询 limit 跳过数量和每页条数
在控制台输入页数和每页的数量 得到对应的内容
如: 输入 页数输入3 每页数量输入 5 则得到 第11-15条数据,
关键点:即分页机制,第一个参数是跳过的页数,第二个参数是每一页多少行记录。
事务(思路)
- 设置自动提交的状态 conn.setAutoCommit(true/false);
- 提交 conn.commit();
- 回滚 conn.rollback();