JDBC使用及其详解

JDBC

学习目标

  1. 理解JDBC相关概念(面试问到的时候,你能通过自己的语言描述说明什么是JDBC)
  2. 能够熟练使用JDBC相关的类和接口

一、初识

1.概念

Java DataBase Connectivity。Java 数据库连接, Java语言操作数据库

2.本质

其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。

在这里插入图片描述

3.步骤

//1.导包(对应数据库的数据库驱动包)  —— mysql-connector-java-5.1.37-bin.jar
	//1. 复制jar包到项目新建的libs目录中(libs和src同级)
	//2. 选中jar包右键build path - add to path
//2. 注册驱动
Class.forName("com.mysql.jdbc.Driver");	
//3.通过驱动获取到数据库连接对象Connection,可以简写jdbc:mysql:///db3
//注意Connetction是导import java.sql.Connection;
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "root");
//4. 定义sql语句
String sql = "update account set balance=10000 where id = 1";
//5. 获取到执行sql的对象 Statement对象,注意要导import java.sql.Statement;
Statement stmt = conn.createStatement();
//6.执行sql,获取返回结果
int count = stmt.executeUpdate(sql);
//7. 处理结果
System.out.println("结果为:"+count);
//8. 释放资源
stmt.close();
conn.close();

4.对象详解

Statement:执行sql的对象:

1.boolean execute(String sql)

​ 可以执行任意的sql语句 了解即可

​ 返回值: 如果第一个结果为 ResultSet 对象,则返回 true;如果其为更新计数或者不存在任何结果,则返回 false

2.int executeUpdate(String sql)

​ 执行给定 DML(INSERT、UPDATE 或 DELETE) 语句或 DDL(create,alter,drop) 语句

​ 返回值:int代表受影响的行数,可以通过该返回值来判断DML语句是否执行成功(返回值>0则成功,反之则失败)如果执行的是DDL语句,成功则返回0.(很少使用)

3.ResultSet executeQuery(String sql)

​ 执行DQL(select)语句

​ 返回值:ResultSet:结果集对象

ResultSet结果集对象

​ boolean next():游标会向下移动一行 返回值:如果新的当前行有效,则返回 true;如果不存在下一行,则返回 false

​ getXxx(参数):获取对应的数据

​ Xxx:代表的是数据类型 eg:getInt() getString()

​ 参数:int:代表列的编号值 从1开始的 getString(1):获取当前光标所在行的第一列的值 int getInt(int columnIndex)

​ String:代表列的名称 getDouble(“balance”) int getInt(String columnLabel)

ResultSet res = stmt.executeQuery(sql);

while(res.next()) {   //判断游标是否位于最后一行
    int id = res.getInt(1);   //int类型的参数,代表第一列
    String name = res.getString("name");   //获取列名叫name的那一列,类型是String(varchar)类型
    Double balance = res.getDouble("balance");
    System.out.println(id+"--->"+name+"--->"+balance);
}

进行封装:查询emp表中的所有数据,并将其封装成一个对象,然后装载到集合中,最后返回集合

res = stmt.executeQuery(sql);

list = new ArrayList<>();
Emp emp = null;

//遍历结果集,将获取到的字段值封装到对象中,对象装载到集合中
while(res.next()) {   
    int id = res.getInt("id");
    String name = res.getString("name");
    String gender = res.getString("gender");
    Double salary = res.getDouble("salary");
    Date date = res.getDate("join_date");
    int deptId = res.getInt("dept_id");

    emp = new Emp();
    emp.setId(id);
    emp.setName(name);
    emp.setGender(gender);
    emp.setSalary(salary);
    emp.setJoinDate(date);
    emp.setDeptId(deptId);

    list.add(emp);

}

二、自定义JDBC工具类

1.抽取JDBC工具类

抽取JDBC工具类:JDBCUtils

  1. 抽取驱动的注册
  2. 抽取连接对象
  3. 将释放资源代码抽取出来
public class JDBCUtils {
	
	private static String driver ;
	private static String url;
	private static String user;
	private static String pwd;
	
	/**
	 * 通过文件的读取,获取到文件中的值,只要读取一次即可. 使用静态代码块实现
	 */
	static {
		try {
			//读取文件资源,获取其中的值
             //新建jdbc.properties 文件
			// 1.创建一个Properties集合类
			Properties pro = new Properties();
			//2.获取加载文件  获取src路径下的文件--->ClassLoader类加载器
			ClassLoader classLoader = JDBCUtils.class.getClassLoader();
			URL res = classLoader.getResource("jdbc.properties");  //获取到src目录下的指定文件
			String path = res.getPath();
			pro.load(new FileReader(path));
			//3.获取数据,赋值
			driver = pro.getProperty("driver");
			url = pro.getProperty("url");
			user = pro.getProperty("user");
			pwd = pro.getProperty("pwd");
			Class.forName(driver);
		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取conn连接对象
	 * @throws SQLException 
	 */
	public static Connection getConnection() throws SQLException {
//		return DriverManager.getConnection("jdbc:mysql:///db3", "root", "123456");
		return DriverManager.getConnection(url,user,pwd);
	}
	
	/**
	 * 释放资源(两个参数)  通过方法重载实现功能的全面性
	 */
	public static void close(Statement stmt,Connection conn) {
		
		close(null, stmt, conn);
	}
	
	/**
	 * 释放资源(三个参数)  通过方法重载实现功能的全面性
	 */
	public static void close(ResultSet rs, Statement stmt,Connection conn) {
		
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

//jdbc.properties:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db3
user=root
password=root

2.使用JDBC工具类

/**
* 使用JDBCUtils工具类
*/
public static List<Emp> findAll2() {

    Connection conn = null;
    Statement stmt =null;
    ResultSet res = null;

    List<Emp> list = null;

    try {
        String sql  = "select * from emp";
        conn = JDBCUtils.getConnection();  //调用工具类连接对象方法,同时会注册驱动
        stmt = conn.createStatement();
        res = stmt.executeQuery(sql);

        list = new ArrayList<>();
        Emp emp = null;

        //遍历结果集,将获取到的字段值封装到对象中,对象装载到集合中
        while(res.next()) {   
            int id = res.getInt("id");
            String name = res.getString("name");
            String gender = res.getString("gender");
            Double salary = res.getDouble("salary");
            Date date = res.getDate("join_date");
            int deptId = res.getInt("dept_id");
            emp = new Emp();
            emp.setId(id);
            emp.setName(name);
            emp.setGender(gender);
            emp.setSalary(salary);
            emp.setJoinDate(date);
            emp.setDeptId(deptId);
            list.add(emp);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        JDBCUtils.close(res, stmt, conn); //调用工具类关闭资源方法
    }

    return list;
}

三、事务

一个包含多个步骤的业务,如果被事务管理,则这多个步骤要么同时成功,要么同时失败

操作:

  1. 开启事务:

    void setAutoCommit(boolean autoCommit):调用该方法时将参数设置为false,即开启事务执行sql语句前开启事务

  2. 提交事务

    void commit():当所有的sql语句执行完后去提交事务。

  3. 回滚事务

    void rollback():一般情况回滚操作会在try catch中的catch块中执行

[案例见](# 事务与PreparedStatement)


四、PreparedStatement

表示预编译的 SQL 语句的对象

PreparedStatement是Statement的子类

为什么要使用PreparedStatement

​ Statement的问题:

​ SQL注入问题: 在拼接sql的时候,有一些特殊的sql的关键字参与字符串的拼接,会引起安全问题

​ 输入一个随意的用户名,输入密码:a’ or ‘a’='a 出现登录成功的操作

​ sql语句:SELECT * FROM USER WHERE USER= ‘12sadadad’ AND pwd=‘a’ OR ‘a’=‘a’ 不管用户名密码正确与否,where都为true

​ PreparedStatement将一些sql中的参数使用占位符(?)替代,后续再为占位符赋值

事务与PreparedStatement

Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;

try {
    //2.3.注册驱动,通过驱动获取到数据库连接对象Connection
    conn = JDBCUtils.getConnection();

    //执行sql前开启事务,调用该方法时将参数设置为false,即开启事务,需要执行sql语句前开启事务
    conn.setAutoCommit(false); 

    //4.定义sql语句  account 表的转账操作
    String sql1 = "update account set balance = balance - ? where id = ?";
    String sql2 = "update account set balance = balance + ? where id = ?";

    //5.获取到执行sql的PreparedStatement对象 与conn.createStatement()区别: 无参与有参(因为要预编译)
    pstmt1 = conn.prepareStatement(sql1);   //将sql1语句预编译
    pstmt2 = conn.prepareStatement(sql2);	//将sql2语句预编译

    //6.为?去赋值
    pstmt1.setDouble(1, 500);	//sql1的第1个?=500
    pstmt1.setInt(2, 1);	//sql1的第二个?=1

    pstmt2.setDouble(1, 500);
    pstmt2.setInt(2, 2);

    //7.执行sql,获取返回结果,此时不需要传递任何sql语句(与Statement区别)
    pstmt1.executeUpdate();  

    int i = 9/0;  //出错

    pstmt2.executeUpdate();

    conn.commit();  //提交事务

} catch (SQLException e) {

    if (conn != null) {
        try {
            conn.rollback();   //进行回滚事务
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
    }
    e.printStackTrace();
}finally {
    JDBCUtils.close(pstmt1, conn);
    JDBCUtils.close(pstmt2, conn);
}
//报错
java.lang.ArithmeticException

五、JDBC连接池

1.概念

其实是一个容器(集合),存放数据库连接的一个容器。

当系统进行初始化的时候,容器会被创建,容器内会自动申请一些连接对象,当用户来访问数据库的时候,从该容器中获取到连接对象,用户使用完数据库后,将连接对象归还给容器.

2.优势

  1. 节约资源,数据库连接对象可以重复利用
  2. 用户访问高效

3.使用

  1. 标准接口:DataSource javax.sql包下的
    1. 方法:
      • Connection getConnection():获取连接
      • Connection.close() 如果Connection对象是从连接池中获取的,Connection.close()方法不会关闭连接,而是会将该连接归还给数据库连接池.
  2. 一般我们不需要去实现DataSource接口,会有专门的数据库厂商帮你实现
    1. C3P0:数据库连接池技术
    2. Druid:Druid(德鲁伊)是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是目前最好的数据库连接池。
//1.创建数据库连接池对象
DataSource ds = new ComboPooledDataSource();

//2.通过连接池对象获取连接对象
Connection conn = null;

PreparedStatement pstmt1 = null;

try {
    conn = ds.getConnection();  //不再需要自定义工具类中的获取连接对象方法

    //执行sql前开启事务
    conn.setAutoCommit(false);  //开启

    //account 表的转账操作
    String sql1 = "update account set balance = balance - ? where id = ?";

    pstmt1 = conn.prepareStatement(sql1);

    pstmt1.setDouble(1, 500);
    pstmt1.setInt(2, 1);

    pstmt1.executeUpdate();  //执行sql
    conn.commit();  //提交事务

} catch (SQLException e) {
    e.printStackTrace();
}finally {
    conn.close();
}

DataSource实现类

1)C3P0

C3P0:数据库连接池技术

步骤:

1.导包(注意build path) 首先要导入数据库驱动包 mysql-connector-java-5.1.37-bin.jar

​ 1.c3p0-0.9.5.2.jar

​ 2.mchange-commons-java-0.2.12.jar

2.定义配置文件:

​ 名称: c3p0.properties 或者 c3p0-config.xml -->自动导入的

c3p0-config.xml

<c3p0-config>
  <!-- 使用默认的配置读取连接池对象 -->
  <default-config>
  	<!--  连接参数 -->
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/db3</property>
    <property name="user">root</property>
    <property name="password">root</property>
    
    <!-- 连接池参数 -->
    <property name="initialPoolSize">5</property> <!-- 默认的连接池的连接数  5 -->
    <property name="maxPoolSize">10</property>  <!-- 最大的连接池的连接数 如果超出最大连接数  会报错  -->
    <property name="checkoutTimeout">3000</property> <!--如果超出最大连接数 3s后    会报错  -->
  </default-config>

  <named-config name="otherc3p0"> 
    <!--  连接参数 -->
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/db3</property>
    <property name="user">root</property>
    <property name="password">root</property>
    
    <!-- 连接池参数 -->
    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">8</property>
    <property name="checkoutTimeout">1000</property>
  </named-config>

</c3p0-config>

c3p0.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///db3
username=root
password=123456
# 默认的连接池的连接数  5
initialSize=5
# 最大的连接池的连接数 如果超出最大连接数10  会报错
maxActive=10
# 如果超出最大连接数 3s后    会报错
maxWait=3000

​ 路径:直接放在src目录即可

3.创建核心对象 CombolPooledDataSource

4.获取连接 getConnection()

/**
 * 使用默认的连接池配置获取连接
 */
public static void testc3p01() throws SQLException{
    //1.获取ds对象
    DataSource ds = new ComboPooledDataSource();
    //2.获取连接
    for (int i = 1; i <= 11; i++) {
        Connection conn = ds.getConnection();  //不用传参
        System.out.println(i+":"+conn);

        if (i==5) {
            conn.close();//归还连接到连接池中
        }
    }
}
/**
  * 使用带name的连接配置创建连接
  */
public static void testc3p02() throws SQLException{
    //1.获取ds对象  使用含有一个configName 的参数
    DataSource ds = new ComboPooledDataSource("otherc3p0");
    //2.获取连接
    for (int i = 1; i <= 9; i++) {
        Connection conn = ds.getConnection();  //不用传参
        System.out.println(i+":"+conn);
    }
}
2)Druid(德鲁伊)

数据库连接池技术

  1. 使用步骤:

    1. 导包:druid-1.0.9.jar 首先要导入数据库驱动包 mysql-connector-java-5.1.37-bin.jar

    2. 定义配置文件

      是properties形式的

      可以叫任意的名称,可以放在任意的位置—>手动导入

    3. 加载配置文件

    4. 获取数据库连接池对象

    5. 获取连接

    //1. 导包:druid-1.0.9.jar  首先要导入数据库驱动包
    //2. 定义配置文件:
    //	* 是properties形式的
    //	* 可以叫任意的名称,可以放在任意的位置--->手动导入
    //3. 加载配置文件
    Properties pro = new Properties();
    
    pro.load(DruidDemo01.class.getClassLoader().getResourceAsStream("druid.properties"));
    
    //4. 获取数据库连接池对象  工厂类获取对象
    DataSource ds = DruidDataSourceFactory.createDataSource(pro);
    //5. 获取连接
    Connection conn = ds.getConnection();
    System.out.println(conn);
    
  2. 定义工具类

    1. 定义一个类

    2. 提供静态代码块加载Druid的配置文件,初始化连接池对象

    3. 提供以下方法

      1. 获取连接对象的方法:通过Druid数据库连接池获取连接对象

      2. 释放资源

      3. 获取连接池的方法

[与之前自定义抽取工具类比较](# 1.抽取JDBC工具类)

public class JDBCDruidUtils {

    //定义成员变量DataSource
    private static DataSource ds ;

    static{

        //加载配置文件并初始化连接池对象
        Properties pro = new Properties();
        try {
            pro.load(JDBCDruidUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
            ds = DruidDataSourceFactory.createDataSource(pro);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    /**
	 * 获取连接对象
	 * @throws SQLException 
	 */
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();  //不用传参
    }

    /**
	 * 释放资源(两个参数)  通过方法重载实现功能的全面性
	 */
    public static void close(Statement stmt,Connection conn) {
        close(null, stmt, conn);
    }

    /**
	 * 释放资源(三个参数)  通过方法重载实现功能的全面性
	 */
    public static void close(ResultSet rs, Statement stmt,Connection conn) {

        if (rs != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
	 * 获取连接池对象
	 */
    public static DataSource getDataSource() {
        return ds;
    }
}
//使用工具类
Connection conn = null;
PreparedStatement pstmt = null;

// 完成添加操作 account表中添加一条记录
try {
    conn = JDBCDruidUtils.getConnection();

    String sql = "insert into account values(null,?,?)";
    pstmt = conn.prepareStatement(sql);
    pstmt.setString(1, "wangwu");
    pstmt.setDouble(2, 5000);
    int count = pstmt.executeUpdate();
    System.out.println(count);

} catch (SQLException e) {
    e.printStackTrace();
}finally {
    JDBCDruidUtils.close(pstmt, conn);
}
# druid.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///db3
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000

六、Spring JDBC

Spring框架对JDBC的简单封装 提供了一个JDBCTemplate对象简化JDBC的开发

实现步骤:

  1. 导包

    commons-logging-1.2.jar

    spring-beans-5.0.0.RELEASE.jar

    spring-core-5.0.0.RELEASE.jar

    spring-jdbc-5.0.0.RELEASE.jar

    spring-tx-5.0.0.RELEASE.jar

  2. 创建jdbcTemplate对象 依赖数据源DataSource

JdbcTemplate template = new jdbcTemplate(ds);

3.操作

private JdbcTemplate template = new JdbcTemplate(JDBCDruidUtils.getDataSource());


//1. 修改id为1的数据的工资为17200
@Test
public void test01() {
    //String sql = "update emp set salary = 17200 where id = 1";
    String sql = "update emp set salary = ? where id = ?";

    //int count = template.update(sql);
    //参数一:sql语句 参数二:代表一个长度可变的参数,重点来为sql中的占位符赋值
    int count = template.update(sql,17200,1);

    System.out.println(count);

}

//2. 为emp表添加一条记录
@Test
public void test02() {
    String sql = "insert into emp(id,name,salary) values(?,?,?)";
    int count = template.update(sql, 6,"红孩儿","300");
    System.out.println(count);
}



//3. 将需求2添加的记录删除掉
@Test
public void test03() {

    String sql = "delete from emp where id = ?";
    int count = template.update(sql,6);
    System.out.println(count);
}

//4. 查询id为1的记录,将其封装到Map集合中 queryForMap():该方法查询的结果集的长度只能是1.
@Test
public void test04() {

    //String sql = "select * from emp where id = ?";
    String sql = "select * from emp where id = ? or id = ?"; //不能这样使用
    //Map<String, Object> map = template.queryForMap(sql, 1);
    Map<String, Object> map = template.queryForMap(sql, 1,2); //错误的
    System.out.println(map);
    //{id=1, NAME=孙悟空, gender=男, salary=7200.0, join_date=2013-02-24, dept_id=1}
}

//5. 查询所有的记录,将其封装到list集合中  queryForList(sql)
@Test
public void test05() {

    String sql = "select * from emp";
    List<Map<String, Object>> list = template.queryForList(sql);
    for (Map<String, Object> map : list) {
        System.out.println(map);
    }

}

//6.查询所有的记录,将其封装为Emp对象的list集合中
@Test
public void test06() {

    String sql = "select * from emp";
    List<Emp> list = template.query(sql, new RowMapper<Emp>() {

        @Override
        public Emp mapRow(ResultSet re, int i) throws SQLException {
            Emp emp = new Emp();
            emp.setId(re.getInt("id"));
            emp.setName(re.getString("name"));
            emp.setSalary(re.getDouble("salary"));
            emp.setGender(re.getString("gender"));
            emp.setJoinDate(re.getDate("join_date"));
            emp.setDeptId(re.getInt("dept_id"));
            return emp;
        }});

    for (Emp emp : list) {
        System.out.println(emp);

    }

}

//6.查询所有的记录,将其封装为Emp对象的list集合中  BeanPropertyRowMapper
@Test
public void test06_02() {

    String sql = "select * from emp";
    List<Emp> list = template.query(sql,new BeanPropertyRowMapper<Emp>(Emp.class));

    for (Emp emp : list) {
        System.out.println(emp);

    }

}


//7.查询总记录数量
@Test
public void test07() {

    String sql = "select count(id) from emp";
    long total = template.queryForObject(sql, Long.class);

    System.out.println(total);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值