第七周 Java语法总结之JDBC大全_java连接数据库基本流程_语句操作_PreparedStatement预编译对象_连接池_测试类_Dbutils工具_事务_工具类

本文详细介绍了Java JDBC连接数据库的流程,包括注册驱动、获取连接、执行SQL语句(DDL、DML、DQL)以及遍历结果集。此外,还探讨了PreparedStatement预编译对象的优势,如防止SQL注入和提高执行效率。接着,讲解了Druid连接池的使用,包括配置文件、创建数据源以及通过连接池获取连接。最后,展示了三层架构思想在数据库操作中的应用,并提及了单元测试和事务管理的重要性。
摘要由CSDN通过智能技术生成

JDBC

在这里插入图片描述

1.JDBC流程
JDBC: Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。
JDBC的本质就是能够实现sun公司提供的java.sql包下的相关的接口的实现类
	数据库厂商提供这一驱动jar包

使用java连接数据库过程:
      1)在java项目添加额外的第三方的jar包
      idea中 file---->  project structure (ctrl+Alt+shift+s:默认快捷键_)---model---->denpendies---->添加---jar包 ---将指定路径拿过来
      2)注册驱动
      3)获取数据库的连接对象
      4)准备静态sql语句
      5)通过数据库连接对象创建执行对象Statement
      6)执行更新操作
      7)释放资源
public class JdbcDemo {
    public static void main(String[] args) throws Exception {
        //1)导包了
        //2)注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver") ; //mysql8.0 的驱动实现类:com.mysql.cj.jdbc.Driver
                                                    //mysql8.0以前的驱动实现类:com.mysql.jdbc.Driver
        //3)获取数据库的连接对象
        //使用驱动管理类 DriverManager:用于管理一组JDBC驱动程序的基本服务。
        //public static Connection getConnection(String url,String user,String password) throws SQLException
        //参数1:url: 连接数据库的路径: 协议名称 + ip地址/域名 :3306/数据库名称   针对mysql server 8.0以前这样使用
        //参数1:url: 连接数据库的路径: 协议名称 + ip地址/域名 :3306/数据库名称?参数1=值2&参数2=值2   针对mysql server 8.0使用

 //?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true",
        //参数2:用户名:root用户
        //参数3:mysql连接的密码
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb_01?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true",
                "root",
                "123456");
        //4)准备sql语句
        String sql = "insert into account(name,balance) values('赵又廷',1000)" ;
        
        //5)通过数据库连接对象获取执行对象
        //public Statement createStatement() ;用于将sql语句发送的数据库的执行器对象
        Statement stmt = connection.createStatement();
        
        //6)执行更新操作
        //通用方法
        //int executeUpdate(String sql) throws SQLException
        int count = stmt.executeUpdate(sql);
        System.out.println("影响了"+count+"行");
        
        //7)释放资源
        stmt.close() ;
        connection.close();
    }
}
1)导包
2)注册驱动
    Class.forName("com.mysql.cj.jdbc.Driver") ; //为了向下兼容   获取当前类的字节码文件对象
    获取类的字节码文件对象com.mysql.cj.jdbc.Driver :Class类对象
    源码
    加载类的时候,就将静态代码块执行了,核心代码就是注册驱动
    static {
            try {
                java.sql.DriverManager.registerDriver(new Driver());//注册驱动
            } catch (SQLException E) {
                throw new RuntimeException("Can't register driver!");
            }
        }
  java.sql.DriverManager
3)通过驱动管理了DrivarManager的getConnection(String url,String user,String password)
     获取Connection
                url:统一资源定位符                             mysql8.0以后就必须带上了
                jdbc:数据库协议://localhost:3306/ 数据库名称 ?参数(本地时区以及当前字符集&useSSL=false(不启用mysql连接认证)...
                    协议          域名或者ip:端口号/数据库名称?参数
                user:用户名
                password:密码
     sun公司提供的 java.sql.Connection:接口
            提供一些方法
                  public Statement   createStatement() :创建执行对象Statement 接口
                  public  PreparedStatement prepareStatement(String sql) :创建预编译对象的同时,发送参数化的sql

java.sql.Statement: 执行对象:将静态的sql发送到数据库中
                每次发送的时都需要执行指定sql
                通用的方法
                int exeuteUpdate(String sql)  :DDL语句:表的操作/以及DML语句insert into ,update,delete
                ResultSet executeQuery(String sql):针对DQL语句:数据库的查询语句
                                              select * from account where id = 值 ;
                dml语句:数据操作语句
                静态sql
                insert into account(name,balance) values('值1',值2) ; (硬编码)
java.sql.prepearedStatement: 继承自   java.sql.Statement: 接口
     预编译对象,发送的sql语句,
            而且在通过连接Connection创建预编译对象的时候就已经将参数化的sql
                insert into account(name,balance) values(?,?) ;
            通用的方法
                 int exeuteUpdate()  :DDL语句:表的操作/以及DML语句insert into ,update,delete
java.sql.ResultSet接口:数据库结果集的数据表
               public boolean next():判断当前光标是否向前(有效数据行)移动(如果有数据,继续移动)
               获取每一列的内容:根据每一个列的字段类型
                    通用的方法
                   XXX 变量名 =  getXXX(列的名称/或者列的索引值) ; 方式1 :通用
                   getMetaData() ----ResultSetMetaData :数据表的元数据信息 方式2
2.操作
1)DDL操作

通过java.sql.Statment接口:针对DDL语句操作:建表语句

public class JdbcDemo {
    public static void main(String[] args) {
        Connection conn  = null ;
        Statement stmt = null ;
        try {
            //注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver") ;
            //创建Connection对象
             conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/ee_2106?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true",
                    "root",
                    "123456");
            //准备sql语句  DDL语句
            String sql = "create table student(" +
                    "id int primary key auto_increment," +
                    " name  varchar(20)," +
                    " gender varchar(10)," +
                    " email varchar(50) )" ;
            //通过连接对象获取执行对象
             stmt = conn.createStatement();
            //发送sql语句到数据库中进行更新
            // int exeuteUpdate(String sql)  :DDL语句:表的操作/以及DML语句insert into ,update,delete
            int count = stmt.executeUpdate(sql);
            //0
            System.out.println(count);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            if(stmt!=null){ //释放执行对象
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(conn!=null){//释放连接对象
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
2)执行DML语句

Statment对象执行静态的sql语句----DML语句 insert into,delete,update

public class JdbcDemo2 {
    public static void main(String[] args) {
        Connection conn  = null ;
        Statement stmt = null ;
        try {
            //注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver") ;
            //创建Connection对象
            conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/ee_2106?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true",
                    "root",
                    "123456");

            //准备sql语句  DML语句
          String sql = "update  student set  gender = '女' where id = 3 " ;
            //通过连接对象获取执行对象
            stmt = conn.createStatement();
            //发送sql语句到数据库中进行更新
            // int exeuteUpdate(String sql)  :DDL语句:表的操作/以及DML语句insert into ,update,delete
            int count = stmt.executeUpdate(sql);
            System.out.println(count);
            System.out.println("修改成功");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            if(stmt!=null){ //释放执行对象
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(conn!=null){//释放连接对象
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
3)遍历SQL
ResultSet接口:一般都是select 查询语句查询的数据表的结果集
  如何操作 select DQL语句
  需求:将ee_2106这个数据库中的student表数据全部查询并遍历出来!

在这里插入图片描述

public class JdbcDemo3 {
    public static void main(String[] args) {
        Connection conn = null ;
        Statement stmt = null ;
        ResultSet rs = null ;
        //注册驱动
        try {
            Class.forName("com.mysql.cj.jdbc.Driver") ;
            //创建数据库连接对象
            conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/ee_2106?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true",
                    "root",
                    "123456");
            //准备sql
            String sql = "select * from student" ;
            //创建执行对象
            stmt = conn.createStatement() ;
            //执行sql语句
            rs = stmt.executeQuery(sql);
            //先判断
            //第一次获取
            if(rs.next()) {
                //如果有下一行数据,那么就获取
                //通用方法
                //XXX getXXX(int columnIndex):列的索引值获取 内容 index从1 开始,第一列值就是1
                //XXX getXXX(String columnIabel):列的名称获取 内容
                int id = rs.getInt(1);
                String name  = rs.getString(2) ;
                String gender = rs.getString(3) ;
                String email = rs.getString(4) ;
                System.out.println(id+"\t"+name+"\t"+gender+"\t"+email);
            }
            while(rs.next()){
                int id = rs.getInt("id");
                String name  = rs.getString("name") ;
                String gender = rs.getString("gender") ;
                String email = rs.getString("email") ;
                System.out.println(id+"\t"+name+"\t"+gender+"\t"+email);
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            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操作的工具类
     频繁的去操作数据库:CRUD:增删查改
     每一条sql都去创建Connection对象
     而且还需不断的去释放资源  Statment对象,Connection,ResultSet集合对象
/*jdbc.properties-----

    url=jdbc:mysql://localhost:3306/ee_2106?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
    driverClass=com.mysql.cj.jdbc.Driver
    user=root
    password=123456
*/
public class JdbcUtils {
    private static String url = null ;
    private static String user = null ;
    private static String password = null ;
    private static String drivceClass = null ;
    //构造方法私有化
    private JdbcUtils(){}
    //静态代码块
    //JdbcUtils工具类一加载就执行静态代码块
    static{
        try {
            //创建一个Properties属性列表
            Properties prop = new Properties() ;
            //读取src下面的jdbc.properties配置文件
            //直接获取当前类的字节码文件对象,获取类加载,获取资源文件所在的输入流对象
            InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
            //将流中内容加载到属性列表中
            prop.load(inputStream);
            System.out.println(prop);//测试属性列表中的内容

            //通过key获取value
            url = prop.getProperty("url") ;
            user = prop.getProperty("user") ;
            password = prop.getProperty("password") ;
            drivceClass = prop.getProperty("driverClass") ;
            //注册驱动
            Class.forName(drivceClass) ;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    //提供功能都是static
    //通用的功能:获取数据库连接对象
    public static Connection getConnection(){
        Connection  conn = null ;
        try {
            //通过驱动类管理类获取
             conn = DriverManager.getConnection(url,user,password) ;
            return conn ;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null ;
    }
    //关闭资源:
    //DDL语句或者DML语句----->Statement对象和Connection
    public static void close(Statement stmt,Connection conn){
       close(null,stmt,conn);
    }
    //关闭资源
    //DQL语句---->ResultSet对象,Statement对象和Connection
    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();
            }
        }
    }
    public static void main(String[] args) {
        Connection connection = JdbcUtils.getConnection();
        System.out.println(connection);
    }
}
3.预编译对象PreparedStatement
1)是否能够防止sq1注入
	Preparedstatement能够有效防止sq1注入(由于存在sq1语句字符串拼接造成sq1注入不安全)
	statement:执行静态sq1语句,语句中就会在字符串拼接,造成sq1注入(不推荐)
	
2)是否高效(提高执行效率)
    Preparedstatement因为操作的参数化的sq1语句,首先预编译---将sq7语句存储在预编译对象中,根据占位符号类型进行赋值setxxx(int parameterIndex ,实际参数(根据当前类型赋值))
    直接通过预编译对执行sq7语句,发送到数据库中进行操作――(执行sq1效率高)
    
    statement对象:每次将拼接好的sq1语句("硬编码")进行执行―(执行sq1效率低)
    每次发送一个新的sq7到数据库中进行执行
    executeupdate(string sq7)
    executeQuery(string sq7)
预编译对象PreparedStatement 继承自Statment对象
操作步骤
      1)注册驱动
      2)获取数据库连接对象
      3)准备参数化的sql   这些sql并非是静态sql语句
      4)通过连接创建预编译对象并将参数化的sql语句保存在PreparedStatement对象中
      5)给参数化的sql的数据进行赋值
      6)执行更新/查询
      7)释放资源
public class PerparedStatementDemo {
    public static void main(String[] args)  {
        Connection conn = null ;
        PreparedStatement stmt = null ;
        ResultSet rs = null ;
        try {
            //获取数据库的连接对象
           conn = Utils.getConnection();
            //准备sql语句:参数化的sql
            //占位符号:?(英文符号)
           // String sql = "insert into student(name,gender,email) values(?,?,?)" ;
            String sql = "select  * from student" ;

            //通过连接对象获取预编译对象并将参数化的sql保存到该对象中
            //PreparedStatement prepareStatement(String sql)
             stmt = conn.prepareStatement(sql); //预编译的过程:对sql语句中占位符号进行描述

            //参数赋值
            //void setXXX(int parameterIndex,XXX num)
            //举例:setString(第几个占位符号(占位符号的索引值:1开始),当前占位符的实际参数"hello" )
         /*   stmt.setString(1,"王桑") ;
            stmt.setString(2,"男") ;
            stmt.setString(3,"wangsang@163.com") ;*/

            //执行
            //ResultSet executeQuery()  :执行查询操作:DQL语句
            //int executeUpdate()  执行通用的更新操作:DDL/DML语句(insert into /update/delete)
           // int count = stmt.executeUpdate();
           // System.out.println("影响了"+count+"行");

            //查询
            rs = stmt.executeQuery();
            while(rs.next()){
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String gender = rs.getString("gender");
                String email = rs.getString("email");
                System.out.println(id+"\t"+name+"\t"+gender+"\t"+email);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            JdbcUtils.close(stmt,conn);
        }
    }
}
使用Statement对象模拟登录操作
     键盘录入用户名和密码,判断是否登录成功
 分析:
      1)创建一个表 user(id,username,password)三个字段
      2)封装一个功能isLogin(String username,String password)---->boolean
      3)利用jdbc基本操作 查询user表即可
      4)通过ResultSet的next()判断,如果查询到了 返回true,否则false!
问题:
      Statement对象操作静态sql语句:硬编码而且由于如果存在sql语句字符串拼接
      造成sql注入问题,用户名和密码不一致,依然登录成功,最不安全的行为!
      select * from user where username='sdfs' and password = 'a 'OR 'a' ='a'   后面恒成立
 
Statement对象和PreparedStatement对比:
	  1)后者防止sql注入,前者存在sql注入(存在sql语句字符串拼接)
      2)执行效率后者高于前者
      前者效率低
           每次获取Statement对象
           executeUpdate(String sql)
           executeQuery(String sql)
 	  后者:
           String sql = "select * from user where username=? and password = ?" ;
           每次获取PreparedStatement对象的时候就已经sql保存在对象中进行预编译过程
            赋值
              executeUpdate()
              executeQuery()
public class Statement_And_PreparedStatementTest {
    public static void main(String[] args) {

        //创建键盘录入对象
        Scanner sc = new Scanner(System.in) ;

        //提示并录入数据
        System.out.println("请输入用户名:");
        String username = sc.nextLine() ;

        System.out.println("请输入密码:");
        String password = sc.nextLine() ;

        //调用一个功能
        boolean flag = isLogin2(username,password) ;
        if(flag){
            System.out.println("恭喜您,登录成功");
        }else{
            System.out.println("登录失败...");
        }
    }

    //PreparedStatement
    //定义登录方法
    public static boolean isLogin2(String username, String password) {
        //获取数据库的连接对象
        Connection conn = null ;
        PreparedStatement stmt = null ;
        ResultSet rs = null ;
        try {
            conn = JdbcUtils.getConnection();

            //准备sql :参数化sql
            //通过用户和密码查询用户
            String sql = "select * from user where username=? and password = ?" ;
            System.out.println(sql);

            //创建执行对象PreparedStatement预编译对象
            stmt = conn.prepareStatement(sql) ;

            //参数赋值
            //在PerparedStatement对象中完成参数赋值过程
            stmt.setString(1,username);
            stmt.setString(2,password);
		   //执行查询
            rs = stmt.executeQuery();
            return rs.next() ;
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.close(rs,stmt,conn);
        }
        return false ;
    }

    //Statement对象
    //定义登录方法
    public static boolean isLogin(String username, String password) {
        //获取数据库的连接对象
        Connection conn = null ;
        Statement stmt = null ;
        ResultSet rs = null ;
        try {
             conn = JdbcUtils.getConnection();

            //准备sql :静态sql
            //通过用户和密码查询用户
            String sql = "select * from user where username='"+username+"' and password = '"+password+"' " ;
            System.out.println(sql);
            //创建执行对象Statement对象
            stmt = conn.createStatement();
            //执行查询
            rs = stmt.executeQuery(sql);
            return rs.next() ;
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.close(rs,stmt,conn);
        }
        return false ;
    }
}
4.连接池Druid

在这里插入图片描述

1.什么是连接池
    是一种容器(集合),存储数据库连接对象;防止用户操作数据库的时候频繁,创建连接对象,而且当数据库连接Connection使用完毕,就释放close()---->消耗内存空间!
    
作用:提供操作数据库性能
	当用户发送请求,需要连接数据库,频繁创建连接对象,还需要释放,这种情况耗时,消耗内存空间大
	启用连接池,用户发送请求,直接通过连接池操作里面已经创建好的连接对象,获取当前线程中的某个Connection,然后直接去操作数据库,使用完毕连接对象之后,从当线程中进行解绑,回到连接池中!
    
    提供固定的可重用的连接对象的容器
连接池的技术:
        c3p0
        dbcp
        druid(推荐)  德鲁伊   阿里的开源项目
        ...
连接池本质就是需要实现sun公司提供的DataSource实现类(物理数据源代替 DriverManager)
   1)导包 druid-1.1.10.jar
                        druid-1.1.10.source.jar:原码包(下载看DruidDataSource原码)
          DruidDataSource 实现类
          需要数据库驱动包:连接数据库mysql-connector-java-8.0.23.jar
   2)准备好数据库连接池的配置文件
        配置文件以.properties :
        druid.properties
        配置文件名称可以任意命名,但是必须在src目录下
   3)读取配置文件
    如何创建DataSource接口对象?
          DruidDataSource-------> 此时会使用工厂模式
          DruidDataSourceFactory工厂类
            提供静态功能:创建数据源DruidDataSource
            public static DataSource createDataSource(Properties properties) throws Exception
/*
    1)导包 数据库驱动包/连接池的包
    2)准备druid.properties
    3)读取druid.properties配置文件
*/

public class DruidDataSourceDemo {
    public static void main(String[] args) throws Exception {
        //创建属性集合列表
        Properties prop = new Properties() ;
        //读druid.properties配置文件
        InputStream inputStream = DruidDataSourceDemo.class.getClassLoader().getResourceAsStream("druid.properties");
        //将druid.properties配置文件加载到属性列表中
        prop.load(inputStream);

        //通过Druid连接池提供的工厂类创建数据源对象DataSource 接口
        //public static DataSource createDataSource(Properties properties) throws Exception
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);

        //底层起始就是 子实现类对象DruidDataSoure
        //Connection getConnection()
        for(int x = 1; x <= 11 ; x ++){
            Connection connection = dataSource.getConnection();
            if(x == 5){
                connection.close(); //并不是真正释放,而是归还到连接池中,等待下一次利用!
            }
            System.out.println(connection) ; //com.mysql.cj.jdbc.ConnectionImpl@2a17b7b6
        }
       // connection.close(); //并不是真正释放,而是归还到连接池中,等待下一次利用!
    }
}
1)工具类
/**
 * 工具类---->DataSource----->获取数据库的连接对象 Connection以及后期管理事务
 * 获取连接对象----静态方法
 * 关闭资源-----静态方法
 */

/*druid.properties:

	driverClassName=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mydb_01?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
    username=root
    password=123456
    #初始化连接数量
    initialSize=5
    #最大激活数量
    maxActive=10
    #最大等待时间(毫秒值,一旦超过3秒就报错了)
    maxWait=3000
*/

public class DruidJdbcUtils {
    //成员变量位置
    private  static DataSource ds ;
    //为了保证线程安全:每一线程使用自己的Connection (张三/李四)
    private static ThreadLocal<Connection>  t1 = new ThreadLocal<>() ; //提供线程的局部变量保存连接对象
    //构造方法私有化
    private DruidJdbcUtils(){}
    //静态代码块
    static{
        try {
            //读取数据库连接池的配置文件----->通过DruidDataSourceFactory工厂类创建DataSource
            //创建一个属性集合列表
            Properties prop = new Properties() ;
            //读取druid.properties
            InputStream inputStream = DruidJdbcUtils.class.getClassLoader().
                    getResourceAsStream("druid.properties");
            //将资源文件所在的输入流加载列表中
            prop.load(inputStream);
            ds = DruidDataSourceFactory.createDataSource(prop); //底层子实现类:DruidDataSource
            //System.out.println("数据源获取成功");

        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //提供静态方法:单独获取数据源
    public static DataSource getDataSource(){
        return ds ;
    }
    //获取连接对象Connection静态功能
    public static Connection getConnection(){
        //从ThreadLocal中获取局部变量的副本:Connection
        /**
         *   public T get()  :从线程中获取局部变量的副本!
         */
        Connection conn =  null ;
        try {
            conn  =  t1.get();
            if(conn==null){
                //如果空,需要从数据库的连接池中获取连接对象
                 conn  = ds.getConnection();
                //获取到之后,每一线程执行自己的Connection
                //将获取到的连接对象 绑定到当前线程中
                t1.set(conn);
            }
            //如果不为空,说明ThreadLocal线程中已经存在Connection
            return conn ; //
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null ;
    }

    //关闭(释放资源)资源
    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();
                //关闭之后,归还到连接池中,需要从当前线程中解绑
                t1.remove();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    public static void close( Statement stmt,Connection conn)  {
        close(null,stmt,conn);
    }

    public static void rollback() {
        try {
            Connection connection = DruidJDBCTUtils.getConnection() ;
            //调用回滚方法
            connection.rollback();
            //使用完毕,归还连接池中
            connection.close();
            //从当前线程中解绑
            t.remove();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
2)三层架构思想

在这里插入图片描述

/* 
	admin管理员实体类
 */
public class Admin {
  /*  FieldTypeComment
    id  int NOT NULL
    usernam evarchar(20) NULL
    gender  varchar(5) NULL
    age int NULL
    address varchar(50) NULL
    phone   varchar(11) NULL*/
  private int id ;
  private String username ;
  private String gender ;
  private int age ;
  private String address ;
  private String phone ;
    public Admin() {
    }
    public Admin(int id, String username, String gender, int age, String address, String phone) {
        this.id = id;
        this.username = username;
        this.gender = gender;
        this.age = age;
        this.address = address;
        this.phone = phone;
    }
    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;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    @Override
    public String toString() {
        return "Admin{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}
/*
	针对Admin业务接口层
 */
public interface AdminService {
    /**
     * 查询所有的Admin管理员实体
     * @return 返回列表数据
     */
    List<Admin> getAllAdmin() ;
    /**
     * 通过id 查询管理员
     * @param id  管理员编号
     * @return  返回实体对象
     */
    Admin getAdmin(int id) ;
    /**
     * 查询总记录数
     * @return
     */
    int getCount() ;
}

/* 
	针对Admin的业务接口实现层
 */
public class AdminServiceImpl implements AdminService {
    @Override
    public List<Admin> getAllAdmin() {
        //创建数据库访问接口对象AdminDao
        AdminDao adminDao = new AdminDaoImpl() ;
        List<Admin> list = adminDao.findAll();
        return list;
    }
    /**
     * 获取指定的管理员
     * @param id  管理员编号
     * @return
     */
    @Override
    public Admin getAdmin(int id) {
        //调用dao层
        AdminDao adminDao = new AdminDaoImpl() ;
        Admin admin = adminDao.selectAdminById(id) ;
        if(admin!=null){
            return admin;
        }else{
            System.out.println("没有查找到管理员");
        }
        return null ;
    }
    @Override
    public int getCount() {
        //调用dao层
        AdminDao adminDao = new AdminDaoImpl() ;
        int count = adminDao.selectTotalCount() ;
        return count;
    }
}
 /* 
 	针对Admin的数据库访问接口层
 */
public interface AdminDao {
    /**
     * 数据库访问的查询所有的管理员列表
     * @return 返回列表数据
     */
    List<Admin> findAll() ;
    /**
     * 根据id编号查询指定的管理员
     * @param id 编号
     * @return 获取实体
     */
    Admin selectAdminById(int id);
    /**
     * 查询总记录数
     * @return
     */
    int selectTotalCount();
}

 /* 
 	针对Admin的数据库访问接口实现层
 */
public class AdminDaoImpl implements AdminDao {
    Connection conn = null ;
    PreparedStatement stmt = null ;
    ResultSet rs = null ;

    @Override
    public List<Admin> findAll() {
        try {
            //获取连接对象
            conn = DruidJdbcUtils.getConnection();
            //准备sql
            String sql ="select * from admin" ;
            //创建预编译对象
            stmt = conn.prepareStatement(sql) ;
            rs = stmt.executeQuery() ;
            //创建List
            List<Admin> list = new ArrayList<>() ;
            //声明admin变量 Admin类型的
            Admin admin = null ;
            while(rs.next()){
                admin = new Admin() ;
                int id = rs.getInt("id");
                String username = rs.getString("username");
                String gender = rs.getString("gender");
                int age = rs.getInt("age");
                String address = rs.getString("address");
                String phone = rs.getString("phone");
                //封装Admin实体
                admin.setId(id);
                admin.setUsername(username);
                admin.setGender(gender);
                admin.setAge(age);
                admin.setAddress(address);
                admin.setPhone(phone);
                //添加到集合中
                list.add(admin) ;
            }
            return list ;
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DruidJdbcUtils.close(rs,stmt,conn);
        }
        return null;
    }

    //查询单个实体
    @Override
    public Admin selectAdminById(int id) {
        //获取数据库的连接对象
        try {
            //获取连接对象
            conn = DruidJdbcUtils.getConnection();
            //准备sql
            String sql ="select * from admin where id = ?" ;
            //创建预编译对象
            stmt = conn.prepareStatement(sql) ;
            //参数赋值
            stmt.setInt(1,id);
            //执行更新
            rs = stmt.executeQuery() ;
            Admin admin = null ;
            while(rs.next()){
                admin = new Admin() ;
                int adminId = rs.getInt("id");
                String username = rs.getString("username");
                String gender = rs.getString("gender");
                int age = rs.getInt("age");
                String address = rs.getString("address");
                String phone = rs.getString("phone");
                //封装
                admin.setId(adminId);
                admin.setUsername(username);
                admin.setGender(gender);
                admin.setAge(age);
                admin.setAddress(address);
                admin.setPhone(phone);
            }
            return admin ;
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DruidJdbcUtils.close(rs,stmt,conn);
        }
        return null;
    }

    @Override
    public int selectTotalCount() {
        //获取数据库的连接对象
        try {
            //获取连接对象
            conn = DruidJdbcUtils.getConnection();
            //准备sql
            String sql ="select * from admin" ; //全部数据
            //创建预编译对象
            stmt = conn.prepareStatement(sql) ;
            //执行更新
            rs = stmt.executeQuery() ;
            //int getRow():ResultSet 获取行数 (第一行1,第二行2)
            int countRow = 0 ;
            while(rs.next()) {
                countRow ++ ;
            }
            return countRow ;

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DruidJdbcUtils.close(rs,stmt,conn);
        }
        return  0 ;
    }
}
public class MyTest {
   /* public static void main(String[] args) {
        //调用业务层Service
        AdminService adminService = new AdminServiceImpl() ;
        List<Admin> list = adminService.getAllAdmin();
        if(list!=null){
            for (Admin admin : list) {
                System.out.println(admin);
            }
        }
    }*/

    //测试查询功能所有
    @Test
    public void testFindAll(){
        //调用service
        AdminService adminService = new AdminServiceImpl() ;
        List<Admin> list = adminService.getAllAdmin();
        if(list!=null){
            for (Admin admin : list) {
                System.out.println(admin);
            }
        }
    }

    //测试查询某个管理员
    @Test
    public void testfindById(){
        //调用service
        AdminService adminService = new AdminServiceImpl() ;
        Admin admin = adminService.getAdmin(2);
        System.out.println(admin);
    }

    //测试查询总记录数
    @Test
    public void testSelectTotalCount(){
        //调用service
        AdminService adminService = new AdminServiceImpl() ;
       int totalCount = adminService.getCount();
        System.out.println(totalCount);
    }
}
5.测试
1.黑盒白盒

在这里插入图片描述

2.单元测试
提供junit的内置注解
   @Test
   		   提供单元测试方法(编写的测试用例,测试某个模块的功能(查询/添加/删除/修改))
   
   @Before标记的方法
           在执行@Test单元测试方法之前先执行  (初始化的操作)
 
   @After标记的方法
           在执行@Test单元测试方法之后执行(一般:资源的关闭)
 
注解本质---->接口
    1)编写一个类
    2)编写一个单元测试方法,不需要参数,也不需要返回值类型
    public void testFindAll()
    3)方法上加入注解@TestDemo
    4)编写测试代码-----  调用serivce 业务代码,获取数据
public class TestDemo {
    //测试查询功能
    @Test
    public void testFindAll(){
        //调用service
        AdminService adminService = new AdminServiceImpl() ;
        List<Admin> list = adminService.getAllAdmin();
        if(list!=null){
            for (Admin admin : list) {
                System.out.println(admin);
            }
        }
    }

    @Before
    public void init(){
        System.out.println("初始化操作...");
    }
    @After
    public void close(){
        System.out.println("释放相关的系统资源");
    }
    
    @Test
    public void testSub(){
        //创建计算器类
        Calculator calculator = new Calculator() ;
        int result = calculator.sub(10, 5);
        //断言进行测试
        //assertArrayEquals:测试数组相关(操作多个数据)
        //assertEquals(预期结果,功能性测试结果)
        Assert.assertEquals(5,result);
    }
}

 /* 
 	计算器类---开发人员开发好的
 */
public class Calculator {
    public int sum(int a,int b){
        return a + b ;
    }
    public int sub(int a, int b){//减法
        return a + b ;
    }
}
6.dbutils
apache提供开源工具类库 commons-dbutils :通用jdbc工具
       将查询的结果或者更新操作进行封装  (PreparedStatement--->executeUpdate()/executeQuery()进行了封装)
  1)导包 核心工具包
       commons-dbutils-1.7.jar
       依赖包  commons-logging:支持相关的
               commons-collections:工具类中涉及其他的api
 
  2)获取数据库的连接对象Connection
  3)获取执行对象:通过物理数据源DataSource:java.sql.DataSource(sun公司提供一个接口)---->替代了DriverManager
           QueryRunner
//基本使用
public class DbUtilsDemo {
    public static void main(String[] args) throws SQLException {
        //1)创建执行对象QueryRunner  (执行对象)
        // public QueryRunner(DataSource ds) { //数据源
        //        super(ds);
        //    }
        //属于自动提交
        QueryRunner queryRunner = new QueryRunner(DruidJdbcUtils.getDataSource()) ;

        //需要给ee_2106的admin表 添加一条数据
        //插入数据
        String sql = "insert into admin(username,gender,age,address,phone) values(?,?,?,?,?)" ;
        //执行通用方法:update()DML语句 ,query()DQL

       // update(String sql,Object...parames):参数1sql 参数2:可变参数,可以使用多个实际参数

        int count = queryRunner.update(sql, "王宝强", "男", 20, "西安市", "13566662222");
        System.out.println(count);
        //不需要关闭资源
    }
}
eg:用户查询
/**
 * 管理员类
 * JavaBean是一种规范:
 *     1)当前类是具体类
 *     2)当前类中字段(成员变量)私有化
 *     3)需要通过公共setXXX()/getXXX()方法
 *     4)当前类可以实现序列化接口
 */
public class Admin  implements Serializable {
    private int id ;
    private String username ;
    private String gender ;
    private int age ;
    private String address ;
    private String phone ;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getUsername() { //getXXX()  get()去掉 username 称为 当前类bean属性
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public int getAge() {
        return age;
    }
    pulic void setAge(int age) {
        this.age = age;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    @Override
    public String toString() {
        return "Admin{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}
/**
 * @author Kuke
 * @date 2021/8/19
 *
 * 针对Admin业务接口
 */
public interface AdminService {
    /**
     * 获取所有管理员列表
     * @return 列表
     */
    List<Admin> getAllAdmin() ;
    /**
     * 查询编号获取Admin实体
     * @param id  编号
     * @return 返回实体对象
     */
    Admin getAdmin(int id) ;
    /**
     * 查询总记录
     * @return
     */
    long getTotal() ;
    /**
     * 根据id修改admin
     * @param admin
     * @return
     */
    int update(Admin admin) ;
}

/**
 * @author Kuke
 * @date 2021/8/19
 * 针对Admin的业务接口实现类
 */
public class AdminServiceImpl implements AdminService {
    @Override
    public List<Admin> getAllAdmin() {
        //调用数据库访问接口
        AdminDao adminDao = new AdminDaoImpl() ;
        List<Admin> list =  adminDao.findAll() ;
        if(list!=null){
            return list ;
        }
        return null ;
    }

    @Override
    public Admin getAdmin(int id) {
        //调用数据库访问接口
        AdminDao adminDao = new AdminDaoImpl() ;
        Admin admin = adminDao.findAdminById(id) ;
        if(admin!=null){
            return admin ;
        }
        return null;
    }

    @Override
    public long getTotal() {
        //调用数据库访问jiekou
        AdminDao adminDao = new AdminDaoImpl() ;
        long count = adminDao.selectTotalCount() ;
        return count;
    }

    @Override
    public int update(Admin admin) {
        //1)修改管理员数据,需要通过id查询Admin
        AdminDao adminDao = new AdminDaoImpl() ;
        //通过编号查询Admin
        Admin selectAdmin = adminDao.findAdminById(admin.getId());
        if(selectAdmin !=null){
            //存在
            //进行修改
            int count = adminDao.updateAdmin(admin) ;
            return  count ;
        }
        return 0;
    }

    public static void main(String[] args) {
        AdminService adminService = new AdminServiceImpl() ;
        Admin admin = new Admin() ;
        admin.setId(3) ;
        admin.setUsername("张三丰");
        admin.setGender("男");
        admin.setAge(20);
        admin.setAddress("南窑国际");
        admin.setPhone("13588889999");
        int count = adminService.update(admin);
        System.out.println(count);
    }
}
/**
 * @author Kuke
 * @date 2021/8/19
 * 针对Admin数据库访问接口
 */
public interface AdminDao {
    /**
     * 获取所有管理员列表
     * @return 列表
     */
    List<Admin> findAll();
    /**
     * 查询编号获取Admin实体
     * @param id  编号
     * @return 返回实体对象
     */
    Admin findAdminById(int id);
    /**
     * 查询总记录
     * @return
     */
    long selectTotalCount();
    /**
     * 修改admin
     * @param selectAdmin
     * @return
     */
    int updateAdmin(Admin selectAdmin);
}

/**
 * @author Kuke
 * @date 2021/8/19
 * 针对Admin的数据库访问接口实现类
 */
public class AdminDaoImpl implements AdminDao {
    @Override
    public List<Admin> findAll() {
        try {
            //创建执行对象
            QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
            //准备sql语句
            String sql = "select *  from admin" ;
            //执行查询
            //query(String sql,ResuletSetHandler handler)
            //参数1:sql
            //参数2:结果集的处理
            //子类:BeanListHandler<T> 将查询的多条记录封装到List集合中
            //List<当前指定的javeBean实体>
            // BeanListHandler<将查询的结果封装实体类型>(当前实体的类Class)
            List<Admin> list = qr.query(sql, new BeanListHandler<Admin>(Admin.class));
            return list ;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }


    //通过id查询Admin实体
    @Override
    public Admin findAdminById(int id) {
        try {
            //创建执行对象
            QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
            //sql
            String sql = "select * from admin where id = ?" ;
            //执行查询
            //query(String sql,ResultSetHandler handler,Object...params)
            //将查询的某一条记录封装到实体类(JavaBean)中,使用到类BeanHandler<T>
            Admin admin = qr.query(sql, new BeanHandler<Admin>(Admin.class), id);
            return admin;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    //查询总记录数
    @Override
    public long selectTotalCount() {
        //QueryRunner执行对象
        try {
            QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
            //sql
            String sql = "select count(id) from admin" ;
            //查询一些单行单列的数据(总记录数),使用单类ScalerHandler<>
            long count = (Long)qr.query(sql, new ScalarHandler<>());
            return count ;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }

    //修改Admin
    @Override
    public int updateAdmin(Admin selectAdmin) {
        try {
            //QueryRunner
            QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
            //sql
            String sql = "update admin set username=? ,gender=? ,age =? ,address =? ,phone =? where id= ?" ;
            //更新操作
            int count = qr.update(sql,
                    selectAdmin.getUsername(),
                    selectAdmin.getGender(),
                    selectAdmin.getAge(),
                    selectAdmin.getAddress(),
                    selectAdmin.getPhone(),
                    selectAdmin.getId());
            System.out.println(count);
            return count ;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }
}
/**
 * @author Kuke
 * @date 2021/8/19
 * 提供一些业务测试 (单元测试)
 */
public class DbutilsTest {
    @Test
    public void testFindAll(){
        //调用业务层方法
        AdminService adminService = new AdminServiceImpl() ;
        List<Admin> list = adminService.getAllAdmin();
        for (Admin admin : list) {
            System.out.println(admin);
        }
    }

    @Test
    public void testFindById(){
        AdminService adminService = new AdminServiceImpl() ;
        Admin admin = adminService.getAdmin(3);
        System.out.println(admin);
    }

    @Test
    public void testSelectTotalCount(){
        AdminService adminService = new AdminServiceImpl() ;
        long count = adminService.getTotal();
        System.out.println(count);
    }

    @Test
    public void testUpdateAdmin(){
        AdminService adminService = new AdminServiceImpl() ;
        //创建Admin
        Admin admin = new Admin() ;
        admin.setId(3) ;
        admin.setUsername("张三丰2");
        admin.setGender("男2");
        admin.setAge(22);

        admin.setAddress("南窑国际2");
        admin.setPhone("13588889888");
        int count = adminService.update(admin);
        System.out.println(count);
    }
}
7.事务
jdbc操作管理事务
  事务:
       一种机制,某个业务中多次调用dao完成多个sql同时执行,要么同时执行成功/要么同时执行失败,否则,就出现数据紊乱! 使用事务管理(JDBC方式操作)
       用户发送请求,通过连接池获取Connection对象
       Connection:管理事务的功能
             void setAutoCommit(boolean autoCommit) throws SQLException
             	默认情况下,新创建的连接对象处于自动提交模式 参数为true,声明为false,禁用自动提交
                需要收到提交sql
             void rollback() throws SQLException:当前执行提交之前,如果sql发生异常,将撤销之前的所有操作
             void commit() throws SQLException:如果执行过程中没有问题或者回滚了,都提交事务,将之前的所有操作永久保存!
/*
 * 需求:mydb_01数据库中的account表
 * zhangsan账户----  lisi账户 转账500
 *
 * 1)获取连接对象
 * 2)准备sql语句
 *      两个update的sql
 *      zhagnsan -500
 *      lisi+ 500
 * 3)分别执行
 * 4)释放资源
 */

public class JdbcTransactionDemo {
    public static void main(String[] args)  {
        Connection connection = null;
        PreparedStatement stmt1 = null;
        PreparedStatement stmt2 = null;
        try {
            //通过DruidJdbcUtils获取连接对象
            connection = DruidJdbcUtils.getConnection();
            //开启事务---将自动提交禁用调用,参数为false,手动提交
            connection.setAutoCommit(false);

            //准备sql语句
            String sql1 = "update account set balance = balance - ? where id  = ?" ;
            //创建预编译对象对sql1进行预编译
            stmt1 = connection.prepareStatement(sql1);
            //参数赋值
            stmt1.setInt(1,500);
            stmt1.setInt(2,1);
            //执行更新
            stmt1.executeUpdate() ;

            //程序出问题了
//            int i = 10 /0 ;

            String sql2 = "update account set balance = balance + ? where id = ? " ;
            //创建预编译对象对sql2进行预编译
            stmt2 = connection.prepareStatement(sql2);
            //参数赋值
            stmt2.setInt(1,500);
            stmt2.setInt(2,2);
            //执行更新
            stmt2.executeUpdate() ;

            //如果没有问题,正常提交
            connection.commit();
            System.out.println("转账成功...");
        } catch (SQLException e) {

            //处理异常: 直接事务回滚
            //有异常,执行catch语句,就回滚
            try {
                System.out.println("程序出问题了...");
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            //e.printStackTrace();
        }
        //释放资源
       DruidJdbcUtils.close(stmt1,connection);
       DruidJdbcUtils.close(stmt2,connection);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值