JDBC

JDBC

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

使用

步骤:

  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. 注册驱动
    Class.forName("com.mysql.jdbc.Driver");
    加载驱动jar包中的Driver类,class对象会自动调用Driver类中的静态代码块
  3. 获取数据库连接对象 Connection
  4. 定义sql
  5. 获取sql语句的执行对象Statement
  6. 执行sql,接受返回结果
  7. 处理结果
  8. 释放资源

注意:idea中使用数据库先要在右侧边栏中开启database

  • 示例代码

    package com.shenguopin.main;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.Statement;
    
    public class jdbctest {
        public static void main(String[] args) throws Exception {
            //导入驱动jar包
            
      	  //注册驱动,mysql5之后的驱动jar包可以省略这步
            Class.forName("com.mysql.jdbc.Driver");
      	  //DriverManager.registerDriver(new Driver()); 这样也行
            
      	  //获取数据库连接对象
            Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "root");
    
      	  //获取执行SQL的对象
            Statement stmt = con.createStatement();
            
      	  //执行SQL
      	  //定义sql语句
            String sql = "SELECT * FROM user";
            ResultSet rs = stmt.executeQuery(sql);
            
      	  //处理结果
            while(rs.next()) {
       			System.out.println(rs.getInt("id") + "\t" + rs.getString("name"));
    		}
            //释放资源
            stmt.close();
            con.close();
            rs.close();
        }
    }
    

各个类详解

  1. DriverManager:驱动管理对象

    1. 注册驱动:告诉程序该使用哪一个数据库驱动jar
      Class.forName("com.mysql.jdbc.Driver");此语句会加载要使用的com.mysql.jdbc.Driver类,并自动调用该类中的静态代码块:

      static {
      	try { java.sql.DriverManager.registerDriver(new Driver());} 
          catch (SQLException E) {throw new RuntimeException("Can't register driver!");}
      }
      

      静态代码块调用了DriverManager的registerDriver方法:static void registerDriver(Driver driver) :注册给定的驱动程序。

      注意:mysql5之后的驱动jar包可以省略注册驱动的步骤。

    2. 获取数据库连接:

      • 方法:static Connection getConnection(String url, String user, String password)

      • url:指定连接的路径

        语法:jdbc:mysql://ip地址(域名):端口号/数据库名称

        例子:jdbc:mysql://localhost:3306/db3

        简写:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称

  2. Connection:数据库连接对象

    1. 功能:
      1. 获取执行sql 的对象
        • 普通执行者对象:Statement createStatement()
        • 预编译执行者对象:PreparedStatement prepareStatement(String sql)
      2. 管理事务:
        • 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
        • 提交事务:void commit()
        • 回滚事务:void rollback()
      3. 释放资源:
        • void close()
  3. Statement:执行sql的对象

    1. 执行sql
      1. boolean execute(String sql) :可以执行任意的sql
      2. 执行增、删、改语句:int executeUpdate(String sql)
        返回值是影响的行数,如果返回值>0的则执行成功
      3. 执行DQL(select)语句:ResultSet executeQuery(String sql)
        返回值ResultSet是一个表。
        注意:sql语句中进行字符串拼接非常麻烦:
    String insert = "insert into student values(1,'李白',23)";//必须把字符串用引号括起来
    
    //字符串变量要这么写
    String name = "李白"。
    String sql = "insert into student values(1,'"+name+"',23)";
    
  4. ResultSet:结果集对象,封装查询结果

    • 判断结果集中是否还有数据:boolean next()
      有数据返回true,并将索引向下移动一行。没有数据返回false
    • XXX getXxx(参数):获取数据
      • Xxx:代表数据类型。如:int getInt(),String getString()
      • 参数:
        1. int:代表列的编号,从1开始,如:getString(1)
        2. String:代表列名称。如:getDouble("balance")
  5. PreparedStatement:执行sql的对象

    1. SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题

      1. 输入用户随便,输入密码:aaa' or 'a' = 'a
      2. sql:select * from user where username = 'fhdsjkf' and password = 'aaa' or 'a' = 'a'
    2. 解决sql注入问题:使用PreparedStatement对象来解决

    3. 预编译的SQL:参数使用?作为占位符,同时解决了sql拼接的麻烦。

    4. 步骤:

      1. 定义sql:注意:sql的参数使用 ?作为占位符。
        如:select * from user where username = ? and password = ?;
      2. 获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(String sql)
      3. 给?赋值:
        • 方法: setXxx(参数1,参数2)
          • 参数1:?的位置编号 从1 开始
          • 参数2:?的值
    5. 注意:后期都会使用PreparedStatement来完成增删改查的所有操作

      1. 可以防止SQL注入
      2. 效率更高
      3. 处理bolb等类型的数据
        String sql = "insert into user(username,photo) values('zs', 图片字节流)";
        //SQL中有blob类型的数据,可以保存图片,但是不能在命令行中插入图片,只能用jdbc操作
      
      1. 获取元数据:就是表头的数据
        String sql = "select * from student";
        PreparedStatement pstmt = connection.PrepareStatement(sql);
        ResultSetMetaData metaData = pstmt.getMetaData();
        int count = metaData.getColumnCount();//获取字段数量
        for(int i = 0; i < count; i++){
           System.out.println(metaData.getColumnName(i + 1));
         }
      
      1. 获取自增长的键值
        /*
         * 我们通过JDBC往数据库的表格中添加一条记录,其中有一个字段是自增的,如果获取这个自增的值呢?
         * PreparedStatement是Statement的子接口。
         * Statement接口中有一些常量值:Statement.RETURN_GENERATED_KEYS。保存了自增的值
         * 获取自增的key值的步骤:
         * (1)PreparedStatement pst = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
         *  (2) 执行sql语句。
         * (3)执行完成sql语句后,通过PreparedStatement的对象调用getGeneratedKeys()方法来获取自增长键值,遍历结果集
         * 		ResultSet rs = pst.getGeneratedKeys();
         */
      
      1. 批处理
        /*
         * 批处理:批量处理sql。可以节约大量时间
         * 例如:一次性向表中添加10000条数据
         * 如何进行批处理操作?
         * (1)在url中要加一个参数
         *     rewriteBatchedStatements=true
         *     那么我们的url就变成了:jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
         * (2)将要一起执行的预编译语句组成一批,然后调用executeBatch()。
         * 		pst.addBatch();
         * 		int[] all = pst.executeBatch();
         * 注意:如果批量添加时,insert使用values,不要使用value
         */
        public class TestBatch {
        	public static void main(String[] args) throws Exception{
        		long start = System.currentTimeMillis();
        		Class.forName("com.mysql.jdbc.Driver");
        		//获取连接,把批处理的命令加入其中
        		String url = "jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true";
        		Connection conn = DriverManager.getConnection(url, "root", "123456");
      
        		String sql = "insert into t_department values(null,?,?)";
        		PreparedStatement pst = conn.prepareStatement(sql);
        		//添加10000数据
        		for (int i = 1; i <=1000; i++) {
        			pst.setObject(1, "模拟部门"+i);
        			pst.setObject(2, "模拟部门的简介"+i);
        			pst.addBatch();//添加到批处理一组操作中,攒一块处理
        			/*if(i % 500 == 0){//有时候也攒一部分,执行一部分
        				//2.执行
        				pst.executeBatch();
        				//3.清空
        				pst.clearBatch();
        			}*/
        		}
        		pst.executeBatch();
        		//4、关闭
        		pst.close();
        		conn.close();
        		long end = System.currentTimeMillis();
        		System.out.println("耗时:" + (end - start));//耗时:821
        	}
        }
      
    6. 示例代码

      package com.shenguopin.main;
      
      import java.sql.*;
      
      public class JDBCDemo05 {
          public static void main(String[] args) {
              Connection conn = null;
              PreparedStatement pstmt = null;
              ResultSet resultSet = null;
              try {
                  //注册驱动
                  try {
                      Class.forName("com.mysql.jdbc.Driver");
                  } catch (ClassNotFoundException e) {
                      e.printStackTrace();
                  }
                  //建立连接
                  conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
                  //写sql语句
                  String sql = "update account set balance=? where id=?";
                  //获取sql语句的PrepareStatement执行对象
                  pstmt = conn.prepareStatement(sql);
                  //给?赋值
                  pstmt.setDouble(1,5000);
                  pstmt.setInt(2,6);
                  //执行sql语句,不用再传参数了,前面已经传过了
                  int count = pstmt.executeUpdate();
                  System.out.println(count);
              } catch (SQLException throwables) {
                  throwables.printStackTrace();
              }finally {
                  if(pstmt!=null){
                      try {
                          pstmt.close();
                      } catch (SQLException throwables) {
                          throwables.printStackTrace();
                      }
                  }
                  if(conn!=null){
                      try {
                          conn.close();
                      } catch (SQLException throwables) {
                          throwables.printStackTrace();
                      }
                  }
              }
      
          }
      }
      

案例

通过jdbc对学生表进行crud操作。

类的层级:

image

  1. DAO层:DAO层主要是做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此,DAO层的设计首先是设计DAO的接口,然后在Spring的配置文件中定义此接口的实现类,然后就可在模块中调用此接口来进行数据业务的处理,而不用关心此接口的具体实现类是哪个类,显得结构非常清晰,DAO层的数据源配置,以及有关数据库连接的参数都在Spring的配置文件中进行配置
  2. Service层:Service层主要负责业务模块的逻辑应用设计。同样是首先设计接口,再设计其实现的类,接着再Spring的配置文件中配置其实现的关联。这样我们就可以在应用中调用Service接口来进行业务处理。Service层的业务实现,具体要调用到已定义的DAO层的接口,封装Service层的业务逻辑有利于通用的业务逻辑的独立性和重复利用性,程序显得非常简洁。
  3. Controller层:Controller层负责具体的业务模块流程的控制,在此层里面要调用Serice层的接口来控制业务流程,控制的配置也同样是在Spring的配置文件里面进行,针对具体的业务流程,会有不同的控制器,我们具体的设计过程中可以将流程进行抽象归纳,设计出可以重复利用的子单元流程模块,这样不仅使程序结构变得清晰,也大大减少了代码量。

数据库和数据表

-- 创建db14数据库
CREATE DATABASE db14;

-- 使用db14数据库
USE db14;

-- 创建student表
CREATE TABLE student(
	sid INT PRIMARY KEY AUTO_INCREMENT,	-- 学生id
	NAME VARCHAR(20),					-- 学生姓名
	age INT,							-- 学生年龄
	birthday DATE						-- 学生生日
);

-- 添加数据
INSERT INTO student VALUES (NULL,'张三',23,'1999-09-23'),(NULL,'李四',24,'1998-08-10'),(NULL,'王五',25,'1996-06-06'),(NULL,'赵六',26,'1994-10-20');

实体类

  • Student类,成员变量对应表中的列
  • 注意:所有的基本数据类型需要使用包装类,以防null值无法赋值
package com.itheima02.domain;

import java.util.Date;

public class Student {
    private Integer sid;
    private String name;
    private Integer age;
    private Date birthday;

    public Student() {
    }

    public Student(Integer sid, String name, Integer age, Date birthday) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                '}';
    }
}

Dao:

package com.example.Dao;

import com.example.domain.Student;

import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ArrayList;

/**
 * @author shenguopin
 * @data 2021/8/11 19:29
 */
public class StudentDaoImpl implements StudentDao{

    @Override
    public ArrayList<Student> findAll() {
        ArrayList<Student> list = new ArrayList<>();
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {

            //1.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db4", "root", "root");
            //3.获取执行对象
            stmt = conn.createStatement();
            //4.执行sql语句
            String sql = "select * from student";
            rs = stmt.executeQuery(sql);
            //5.处理结果集
            while(rs.next()){
                int id = rs.getInt("sid");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                Date birthday = rs.getDate("birthday");
                Student student = new Student(id,name,age,birthday);
                list.add(student);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.释放资源
            if(rs!=null){
                try {
                    rs.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(stmt!=null){
                try {
                    stmt.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        return list;
    }

    @Override
    public Student findById(Integer sid) {
        Student stu = null;
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {

            //1.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db4", "root", "root");
            //3.获取执行对象
            stmt = conn.createStatement();
            //4.执行sql语句
            String sql = "select * from student where sid ='"+sid+"'";
            rs = stmt.executeQuery(sql);
            //5.处理结果集
            while(rs.next()){
                int id = rs.getInt("sid");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                Date birthday = rs.getDate("birthday");
                stu =  new Student(id,name,age,birthday);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.释放资源
            if(rs!=null){
                try {
                    rs.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(stmt!=null){
                try {
                    stmt.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        return stu;
    }

    @Override
    public int insert(Student stu) {
        Connection conn = null;
        Statement stmt = null;
        int result = 0;
        try {

            //1.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db4", "root", "root");
            //3.获取执行对象
            stmt = conn.createStatement();
            //4.执行sql语句
            Integer sid = stu.getSid();
            String name = stu.getName();
            Integer age = stu.getAge();
            Date d = stu.getBirthday();//日期类需要格式化
            SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd");
            String birthday = sdf.format(d);
            //注意insert语句的写法,需要把sid等变量全部拼接成字符串才行
            String sql = "insert into student values('"+sid+"','"+name+"','"+age+"','"+birthday+"')";
            result = stmt.executeUpdate(sql);
            //5.处理结果集


        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.释放资源
            if(stmt!=null){
                try {
                    stmt.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        return result;
    }

    @Override
    public int delete(Integer id) {
        Connection conn = null;
        Statement stmt = null;
        int result = 0;
        try {

            //1.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db4", "root", "root");
            //3.获取执行对象
            stmt = conn.createStatement();
            //4.执行sql语句
            String sql = "delete from student where sid = "+id;
            result = stmt.executeUpdate(sql);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.释放资源
            if(stmt!=null){
                try {
                    stmt.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        return result;
    }

    @Override
    public int update(Student stu) {
        Connection conn = null;
        Statement stmt = null;
        int result = 0;
        try {

            //1.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db4", "root", "root");
            //3.获取执行对象
            stmt = conn.createStatement();
            //4.执行sql语句
            String sql = "update student set sid='"+stu.getSid()+"',name='"+stu.getName()+"',age='"+stu.getAge()+"',birthday='"+stu.getBirthday()+"' where sid = '"+stu.getSid()+"'";
            result = stmt.executeUpdate(sql);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.释放资源
            if(stmt!=null){
                try {
                    stmt.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        return result;
    }
}

Service:

package com.example.service;

import com.example.Dao.StudentDao;
import com.example.Dao.StudentDaoImpl;
import com.example.domain.Student;

import java.util.ArrayList;

/**
 * @author shenguopin
 * @date 2021/8/11 19:53
 */
public class StudentServiceImpl implements StudentService{
    private StudentDao dao = new StudentDaoImpl();
    @Override
    public ArrayList<Student> findAll() {
        return dao.findAll();
    }

    @Override
    public Student findById(Integer sid) {
        return dao.findById(sid);
    }

    @Override
    public int insert(Student stu) {
        return dao.insert(stu);
    }

    @Override
    public int delete(Integer id) {
        return dao.delete(id);
    }

    @Override
    public int update(Student stu) {
        return dao.update(stu);
    }
}

controller

package com.example.controller;

import com.example.domain.Student;
import com.example.service.StudentService;
import com.example.service.StudentServiceImpl;
import org.junit.Test;

import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.Date;

/**
 * @author shenguopin
 * @date 2021/8/11 20:47
 */
public class StudentController {
    private StudentService service = new StudentServiceImpl();

    //查询所有学生信息
    @Test
    public void findAll(){
        ArrayList<Student> list = service.findAll();
        for (Student student : list) {
            System.out.println(student);
        }
    }
    //条件查询
    @Test
    public void findById(){
        Student stu = service.findById(3);
        System.out.println(stu);
    }

    //添加学生信息
    @Test
    public void insert(){
        Student stu = new Student(5,"周七",27,new Date());
        int result = service.insert(stu);
        if(result!=0){
            System.out.println("添加成功");
        }else{
            System.out.println("添加失败");
        }
    }

    //删除学生信息
    @Test
    public void delete(){
        int result = service.delete(5);
        if(result!=0){
            System.out.println("删除成功");
        }else{
            System.out.println("删除失败");
        }
    }

    //修改学生信息
    @Test
    public void update(){
        Student stu = service.findById(5);
        stu.setName("周七七");
        int result = service.update(stu);
        if(result!=0){
            System.out.println("修改成功");
        }else{
            System.out.println("修改失败");
        }
    }
}

JDBCUtils工具类

工具类代码


/**
 * JDBC工具类,简化驱动设置,获取连接,释放资源等重复操作
 */
import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @author shenguopin
 * @date 2021/9/8 22:03
 */
public class JDBCUtils {

    private static DataSource dataSource;
    private static ThreadLocal<Connection> threadLocal = new ThreadLocal();//这个类可以在同一个线程中共享变量
    private JDBCUtils(){}

	//类加载的时候自动读取数据库连接的配置信息
    static{
        try{
            InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
            Properties properties = new Properties();
            properties.load(is);
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        }catch (Exception e) {
            e.printStackTrace();
        }

    }

    //返回数据库连接的方法
    public static Connection getConnection(){
        Connection connection = threadLocal.get();//优先使用同一个线程中共享的变量
        if(connection == null){//如果同一线程中没有共享的连接
            try {
                connection = dataSource.getConnection();
                threadLocal.set(connection);//添加一个共享连接
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return connection;
    }

    //关闭连接的方法
    public static void close(Connection connection, Statement stmt, ResultSet rs){
        if(connection != null){
            try {
                connection.close();
                threadLocal.remove();//清除掉线程中共享的连接
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    public static void close(Connection connection,Statement stmt){
        close(connection,stmt,null);
    }
    //通用的增删改方法
    public static int CommonUpdate(String sql,Object...objs){
        int result = 0;
        Connection connection = null;
        PreparedStatement pstmt = null;
        try {
            connection = dataSource.getConnection();
            pstmt = connection.prepareStatement(sql);
            for(int i = 0; i < objs.length; i ++){
                pstmt.setObject(i+1,objs[i]);
            }
            result = pstmt.executeUpdate();

            ResultSetMetaData metaData = pstmt.getMetaData();
            int count = metaData.getColumnCount();
            for(int i = 0; i < count; i++){
                System.out.println(metaData.getColumnName(i + 1));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            close(connection,pstmt,null);
        }
        return result;
    }

    //查询的方法,返回的结果包装成对象列表
    public static <T> List<T> get(Class<T> cls, String sql,Object...args) throws InstantiationException, IllegalAccessException {
        List<T> list = new ArrayList<>();

        Connection connection = JDBCUtils.getConnection();
        PreparedStatement pstmt = null;
        ResultSet resultSet = null;
        try {
            pstmt = connection.prepareStatement(sql);
            //设置sql的参数
            if(args != null && args.length > 0){
                for(int i = 0; i < args.length; i ++){
                    pstmt.setObject(i+1,args[i]);
                }
            }
            //获取结果集
            resultSet = pstmt.executeQuery();
            //获取元数据
            ResultSetMetaData metaData = pstmt.getMetaData();
            int columnCount = metaData.getColumnCount();
            //遍历结果集
            while(resultSet.next()){
                T t = cls.newInstance();
                for(int i = 0; i < columnCount; i ++){
                    //获取字段名
                    String columnLabel = metaData.getColumnLabel(i + 1);
                    //根据字段名获取属性并为对象赋值
                    Field field = cls.getDeclaredField(columnLabel);
                    Object value = resultSet.getObject(columnLabel);

                    field.setAccessible(true);//private成员都需要这一步
                    field.set(t,value);
                }
                //将对象添加到列表
                list.add(t);
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }


        return list;
    }

}


## JDBC控制事务

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

2. 操作:

   1. 开启事务
   2. 提交事务
   3. 回滚事务

3. 使用Connection对象来管理事务

   * 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
     * 在执行sql之前开启事务
   * 提交事务:commit() 
     * 当所有sql都执行完提交事务
   * 回滚事务:rollback() 
     * 如果出现了错误,就在catch中回滚事务

4. 示例代码

   ```java
   //jdbc事务操作
   public class JDBCDemo06 {
       public static void main(String[] args) {
           Connection conn = null;
           PreparedStatement pstmt1 = null;
           PreparedStatement pstmt2 = null;
   
           try {
               //获取连接
               conn = JDBCUtils.getConnection();
               //开始事务
               conn.setAutoCommit(false);
               //sql语句
               //张三向李四转账500
               String sql1 = "UPDATE account set balance=balance - ? WHERE id = ?";
               String sql2 = "UPDATE account set balance=balance + ? WHERE id = ?";
               //创建sql语句的执行对象
               pstmt1 = conn.prepareStatement(sql1);
               pstmt2 = conn.prepareStatement(sql2);
               //设置?的参数
               pstmt1.setDouble(1,500);
               pstmt1.setInt(2,1);
               pstmt2.setDouble(1,500);
               pstmt2.setInt(2,2);
               //执行sql
               pstmt1.executeUpdate();
               //手动制造异常
               int i = 1/0;
               pstmt2.executeUpdate();
               //如果程序无错误,会执行下面的提交事务
               conn.commit();
           } catch (Exception e){
               try {
                   if(conn!=null){
                   conn.rollback();
                   }
               } catch (SQLException e1) {
                   e1.printStackTrace();
               }
               e.printStackTrace();
           }finally {
               JDBCUtils.close(pstmt1,conn);//这里用到了多态,PrepareStatement是Statement的子类
               JDBCUtils.close(pstmt2,conn);
           }
       }
   }

JDBC连接池

  1. 概念:其实就是一个容器(集合),存放数据库连接对象。当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。

  2. 好处:

    1. 节约资源
    2. 用户访问高效
  3. 实现:

    1. 标准接口:javax.sql包下的DataSource

      1. 方法:
        • 获取连接:getConnection()
        • 归还连接:Connection.close()。如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了。而是归还连接
    2. 一般我们不去实现它,有数据库厂商来实现

      1. C3P0:数据库连接池技术
      2. Druid:数据库连接池实现技术,由阿里巴巴提供的
  4. C3P0:数据库连接池技术

    • 步骤:

      1. 导入jar包 (两个) c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar ,注意:不要忘记导入数据库驱动jar包
      2. 定义配置文件:
        名称:c3p0.properties 或者 c3p0-config.xml
        路径:直接将文件放在src目录下即可
      3. 创建核心对象,数据库连接池对象ComboPooledDataSource
      4. 获取连接:getConnection
    • 代码:

      package dataSource;
      
      import com.mchange.v2.c3p0.ComboPooledDataSource;
      
      import javax.sql.DataSource;
      import java.sql.Connection;
      import java.sql.SQLException;
      
      //c3po连接池演示
      public class C3P0Demo {
          public static void main(String[] args) {
              //1.创建数据库连接池对象
              DataSource ds = new ComboPooledDataSource();
              //2.获取连接池对象,最多可以获取maxPoolSize(默认10个)个连接对象
              for (int i = 0; i < 11; i++) {
                  Connection conn = null;
                  try {
                      conn = ds.getConnection();
                  } catch (SQLException throwables) {
                      throwables.printStackTrace();
                  }
                  System.out.println(i+":"+conn);
              }
      }
      
      
  5. Druid:数据库连接池实现技术,由阿里巴巴提供的

    1. 步骤

      1. 导入jar包 druid-1.0.9.jar
      2. 定义配置文件:
        是properties形式的,名称随意,目录随意
      3. 手动加载配置文件
      4. 获取数据库连接池对象:通过工厂来来获取DruidDataSourceFactory
      5. 获取连接:getConnection
    2. 代码

      package dataSource;
      
      import com.alibaba.druid.pool.DruidDataSourceFactory;
      
      import javax.sql.DataSource;
      import java.io.IOException;
      import java.io.InputStream;
      import java.sql.Connection;
      import java.util.Properties;
      
      public class DruidDemo {
          public static void main(String[] args) throws Exception {
              //导入jar包和配置文件
              //加载配置文件
              Properties prop = new Properties();
              InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
              prop.load(is);
              //获取连接池对象
              DataSource ds = DruidDataSourceFactory.createDataSource(prop);
              //获取连接
              Connection conn = ds.getConnection();
              System.out.println(conn);
        	  
        	  /*DruidDataSource ds = new DruidDataSource();
        	  *基本配置
        	  *ds.setUsername("root");
        	  *ds.setPassword("root");
        	  *ds.setUrl("jdbc:mysql:///test");
        	  *ds.setDriverClassName("com.mysql.jdbc.Driver");
        	  *连接池配置
        	  *ds.setInitialSize(5);初始化5条连接
        	  *ds.setMaxActive(10);最大10条连接
        	  *ds.setMaxWait(2000);最长等待时间
        	  *DruidPooledConnection conn = ds.getConnection();
        	  */
          }
      }
      
      
    3. 定义工具类DruidJDBCUtils

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

      2. 提供方法

        1. 获取连接池的方法
        2. 获取连接对象的方法
        3. 释放资源
      3. 示例代码

        /*
            数据库连接池工具类
         */
        public class DataSourceUtils {
            //1.私有构造方法
            private DataSourceUtils(){}
        
            //2.定义DataSource数据源变量
            private static DataSource dataSource;
        
            //3.提供静态代码块,完成配置文件的加载和获取连接池对象
            static {
                try{
                    //加载配置文件
                    InputStream is = DruidDemo1.class.getClassLoader().getResourceAsStream("druid.properties");
                    Properties prop = new Properties();
                    prop.load(is);
        
                    //获取数据库连接池对象
                    dataSource = DruidDataSourceFactory.createDataSource(prop);
        
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
        
            //4.提供获取数据库连接的方法
            public static Connection getConnection() {
                Connection con = null;
                try {
                    con = dataSource.getConnection();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                return con;
            }
        
            //5.提供获取数据库连接池的方法
            public static DataSource getDataSource() {
                return dataSource;
            }
        
            //6.提供释放资源的方法
            public static void close(Connection con, Statement stat, ResultSet rs) {
                if(con != null) {
                    try {
                        con.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
        
                if(stat != null) {
                    try {
                        stat.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
        
                if(rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        
            public static void close(Connection con, Statement stat) {
                close(con,stat,null);
            }
        
        }
        
        

Spring JDBC

连接池简化了连接对象的创建,工具类有进一步简化了,但是编写sql语句的步骤还是有些麻烦。Sprint框架提供的JDBCTemplate类可以对JDBC进一步简化,尤其是在编写sql语句时非常方便。

步骤

  1. 导入jar包
  2. 创建JdbcTemplate对象。依赖于数据源DataSource
    JdbcTemplate template = new JdbcTemplate(ds);
  3. 调用JdbcTemplate的方法来完成对数据库的操作
    • update()
    • queryForMap()将结果集封装为map集合,只能用在单行的结果集上,列名为Key,值为value
    • queryForList():将结果集封装为list集合,每一条记录是一个Map,再将Map装到List中
    • query():将结果封装为JavaBean对象
      参数:RowMapper接口
      Spring提供的实现类:BeanPropertyRowMapper
      new BeanPropertyRowMapper<类名>(类名.class)
    • queryForObject:查询结果封装成对象,一般用于聚合函数的查询
package cn.itcast.jdbctemplate;

import cn.itcast.domain.Emp;
import cn.itcast.utils.JDBCUtils;
import org.junit.Test;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

public class JdbcTemplateDemo2 {

    //Junit单元测试,可以让方法独立执行


    //1. 获取JDBCTemplate对象
    private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
    /**
     * 1. 修改1号数据的 salary 为 10000
     */
    @Test
    public void test1(){

        //2. 定义sql
        String sql = "update emp set salary = 10000 where id = 1001";
        //3. 执行sql
        int count = template.update(sql);
        System.out.println(count);
    }

    /**
     * 2. 添加一条记录
     */
    @Test
    public void test2(){
        String sql = "insert into emp(id,ename,dept_id) values(?,?,?)";
        int count = template.update(sql, 1015, "郭靖", 10);
        System.out.println(count);

    }

    /**
     * 3.删除刚才添加的记录
     */
    @Test
    public void test3(){
        String sql = "delete from emp where id = ?";
        int count = template.update(sql, 1015);
        System.out.println(count);
    }

    /**
     * 4.查询id为1001的记录,将其封装为Map集合
     * 注意:这个方法查询的结果集长度只能是1
     */
    @Test
    public void test4(){
        String sql = "select * from emp where id = ? or id = ?";
        Map<String, Object> map = template.queryForMap(sql, 1001,1002);
        System.out.println(map);
        //{id=1001, ename=孙悟空, job_id=4, mgr=1004, joindate=2000-12-17, salary=10000.00, bonus=null, dept_id=20}

    }

    /**
     * 5. 查询所有记录,将其封装为List
     */
    @Test
    public void test5(){
        String sql = "select * from emp";
        List<Map<String, Object>> list = template.queryForList(sql);

        for (Map<String, Object> stringObjectMap : list) {
            System.out.println(stringObjectMap);
        }
    }

    /**
     * 6. 查询所有记录,将其封装为Emp对象的List集合
     */

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

            @Override
            public Emp mapRow(ResultSet rs, int i) throws SQLException {
                Emp emp = new Emp();
                int id = rs.getInt("id");
                String ename = rs.getString("ename");
                int job_id = rs.getInt("job_id");
                int mgr = rs.getInt("mgr");
                Date joindate = rs.getDate("joindate");
                double salary = rs.getDouble("salary");
                double bonus = rs.getDouble("bonus");
                int dept_id = rs.getInt("dept_id");

                emp.setId(id);
                emp.setEname(ename);
                emp.setJob_id(job_id);
                emp.setMgr(mgr);
                emp.setJoindate(joindate);
                emp.setSalary(salary);
                emp.setBonus(bonus);
                emp.setDept_id(dept_id);

                return emp;
            }
        });


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

    /**
     * 6. 查询所有记录,将其封装为Emp对象的List集合
     */

    @Test
    public void test6_2(){
        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 test7(){
        String sql = "select count(id) from emp";
        Long total = template.queryForObject(sql, Long.class);
        System.out.println(total);
    }

}

Apache的DBUtils

commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。

其中QueryRunner类封装了SQL的执行,是线程安全的。

(1)可以实现增、删、改、查、批处理、

(2)考虑了事务处理需要共用Connection。

(3)该类最主要的就是简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。

(1)更新

public int update(Connection conn, String sql, Object... params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。

......

(2)插入

public T insert(Connection conn,String sql,ResultSetHandler rsh, Object... params) throws SQLException:只支持INSERT语句,其中 rsh - The handler used to create the result object from the ResultSet of auto-generated keys. 返回值: An object generated by the handler.即自动生成的键值

....

(3)批处理

public int[] batch(Connection conn,String sql,Object[][] params)throws SQLException: INSERT, UPDATE, or DELETE语句

public T insertBatch(Connection conn,String sql,ResultSetHandler rsh,Object[][] params)throws SQLException:只支持INSERT语句

.....

(4)使用QueryRunner类实现查询

public Object query(Connection conn, String sql, ResultSetHandler rsh,Object... params) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理 PreparedStatement 和 ResultSet 的创建和关闭。

....

ResultSetHandler接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。ResultSetHandler 接口提供了一个单独的方法:Object handle (java.sql.ResultSet rs)该方法的返回值将作为QueryRunner类的query()方法的返回值。

该接口有如下实现类可以使用:

  • BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
  • BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
  • ScalarHandler:查询单个值对象
  • MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
  • MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
  • ColumnListHandler:将结果集中某一列的数据存放到List中。
  • KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
  • ArrayHandler:把结果集中的第一行数据转成对象数组。
  • ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
posted @ 2021-08-29 16:42  黄了的韭菜  阅读(56)  评论(0编辑  收藏  举报