(二十二)JDBC,连接池,重构思想

JDBC

jdbc是一个组件,能都被多种数据库访问,由java语言编写的类和接口组成

为什么要学习JDBC规范

JDBC是java连接数据库的一个标准,由数据库各个厂商来完成接口的实现


JDBC执行规范

  1. 注册驱动,加载JDBC驱动
  2. 获取连接对象
  3. 获取预编译语句对象preparedStatement
  4. 执行SQL语句
  5. 释放资源
1.Class.forName(com.mysql.jdbc.Driver);
2.Connection connection =DriverManager.getConnection("jdbc:mysql://localhost:3306/数据库名称","root(用户名)","密码")
//如果是本机端口为3306,可以省略
Connection connection =DriverManager.getConnection("jdbc:mysql:///数据库名称","root(用户名)","密码")
// ? 使用的是占位符来进行拼接
3.String sql = "update t_student set name=? ,age=?,email=? where id=?"
//获取预编译对象,传入sql
4.PreparedStatement statement = connection.prepareStatement(sql);
//执行
statement.executeUpdate();
//释放资源
statement.close();
connection.close();

在使用PreparedStatement 调用executeUpdate()等操作时,最好不要传入sql语句


Statement 和PreparedStatement的区别

  • PreparedStatement提供更好的性能(预编译)
  • PreparedStatement更安全,可以防止SQL注入问题 ,使用 ?占位符,提前预编译到数据库中,语句结构固定下来

DAO思想

DAO(Data Acess Object ):数据访问对象 (面向对象的数据库接口),自己定义接口与实现实现类的设计思想,用于封装减少重复代码,DAO其实就是一个组件(可以重复使用)


定义DAO包

使用倒置域名定义DAO包

  • cn.k.dao: //定义dao包,用来定义类的接口
dao包中接口的命名规范  IXxxDAO
  • cn.k.dao.impl : //定义impl包,拿来装dao包中接口的实现类
impl包中的实现类命名规范 XXXDAOImpl
  • cn.k.domain: //定义domain包,用来放类的对象
  • cn.k.util :工具包,用来放工具类
  • cn.k.test: 放置测试类


DAO查询操作

ResultSet接口 :通过执行DQL语句查询之后的结果对象

ResultSet对象具有指向其当前数据行的光标.next()方法将光标移动到下一行,移到下一行时(上一行的数据会被覆盖)如果Result对象没有下一行时返回false,所以可以使用while循环进行迭代

例子:

  • 实体类
package cn.k.domain;
@Data
public class Student {
     
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
  • 定义接口
public  interface IStudentDAO {
     
    //操作数据库的增删改查方法

    //添加
    void insert(Student student);

    //删除
    void deleteById(Long id);

    //修改
    void update(Student student);

    //查询
    Student selectById(Long id);
    //查询所有
    List<Student> selectAll();


}
  • 定义实现类(其中一部分)
public class StudentDAOImpl implements IStudentDAO {
     
@Override
    public Student selectById(Long id) {
     
        Student student = new Student();
        String sql = "select*from t_student where id=?";
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
     
            //加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            //获取连接对象
            connection = DriverManager.getConnection("jdbc:mysql:///product", "root", "123");
            //获取预编译对象
            statement = connection.prepareStatement(sql);
            //设置参数
            statement.setLong(1, id);
            //发送 数据到数据库服务器
            resultSet = statement.executeQuery();
            //迭代结果集
            while (resultSet.next()) {
     
                long id1 = resultSet.getLong("id");
                String name = resultSet.getString("name");
                int age = resultSet.getInt("age");
                String email = resultSet.getString("email");
                student.setId(id1);
                student.setName(name);
                student.setAge(age);
                student.setEmail(email);


            }

        } catch (Exception e) {
     
            e.printStackTrace();
        } finally {
     
            //关闭资源
            if (statement != null) {
     
                try {
     
                    statement.close();
                } catch (SQLException e) {
     
                    e.printStackTrace();
                }

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

        }
        return student;
    }
    
}


重构思想

对代码进行优化,即重构,从代码量来讲更加简约,将相同构造的代码抽取出来,进行分类

使用JDBC的规范来实现接口

代码的重构

1.对资源释放进行抽取; 2.对连接对象进行抽取 ; 3.对连接数据库的参数进行了抽取

  • 扩展

4.对DML操作进行模板化实现,达到代码的复用性目的

​5.对DQL操作进行模板化实现,达到代码的复用性目的


对teacher类定义增删改查方法查询数据库(重构)


- 定义工具类Util包

  • JDBCUtil (封装关闭异常方法,加载数据库配置文件properties)
package cn.kent.util;

import java.sql.*;
import java.util.Properties;

public final class JDBCUtil {
     
    private JDBCUtil(){
     }
     private  static Properties properties=new Properties();
    //静态代码块
    static {
     
        try {
     
            //加载配置文件
          properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
            //加载驱动,只加载一次
            Class.forName("com.mysql.jdbc.Driver");
        } catch (Exception e) {
     
            e.printStackTrace();
        }
    }

        public static Connection getConnection(){
     
          //获取连接对象
            Connection connection =null;
            try {
     
               connection= DriverManager.getConnection(
                  properties.getProperty("url"),
                  properties.getProperty("username"),
                  properties.getProperty("password")
                );
            } catch (SQLException e) {
     
                e.printStackTrace();
            }
            return connection;
        }


       //释放资源
       public static void release(PreparedStatement statement, Connection connection, ResultSet resultSet){
     

           if (statement != null) {
     
               try {
     
                   statement.close();
               } catch (SQLException e) {
     
                   e.printStackTrace();
               }
           }
           if (connection != null) {
     
               try {
     
                   connection.close();
               } catch (SQLException e) {
     
                   e.printStackTrace();
               }
           }
           if (resultSet != null) {
     
               try {
     
                   resultSet.close();
               } catch (SQLException e) {
     
                   e.printStackTrace();
               }
           }
       }
    //释放资源,重载关闭两个资源
    public static void release(PreparedStatement statement, Connection connection){
     
           release(statement,connection,null);
    }
}
  • DQLUtil类,(编写工具方法executeUpdate,executeQuery等)
public abstract class DQLUtil {
     
    //编写DML方法,传入SQL语句,与可变参数
    public static void executeUpdate(String sql,Object...objects){
     
        Connection connection=null;
        PreparedStatement statement=null;

        try {
     
            //获取连接对象,调用JDBCUtil中的getConnection()获取到连接对象
            connection=JDBCUtil.getConnection();
            //获取语句对象
            statement=connection.prepareStatement(sql);
            //设置参数
            for (int i = 0; i < objects.length; i++) {
     
                statement.setObject(i+1,objects[i]);
            }
            //执行sql的DML方法
            statement.executeUpdate();
         
        } catch (Exception e) {
     
            e.printStackTrace();
        }finally {
     
            //释放资源
            JDBCUtil.release(statement,connection);
       }
    }


    //查询方法 ,定义泛型 ,提供字节码 ,自定义接口ResultSeHandler
    public static <T>  List<T> executeQuery(String sql, ResultSeHandler handler,Class<T> als, Object...objects)  {
     
        List<T> list =new ArrayList<>();
        Connection connection=null;
        PreparedStatement statement=null;
        ResultSet resultSet=null;
        try {
     
            //获取连接对象
            connection=JDBCUtil.getConnection();
            //获取语句对象
            statement=connection.prepareStatement(sql);
            //设置参数
            for (int i = 0; i < objects.length; i++) {
     
                statement.setObject(i+1,objects[i]);
            }
            //执行sql
            resultSet =statement.executeQuery();
            //需要进行返回值,调用接口实现中的方法
            List list1 = handler.handleResultSet(resultSet, als);
            return list1;
          } catch (Exception e) {
     
            e.printStackTrace();
        }finally {
     
            JDBCUtil.release(statement,connection,resultSet);
        }

        return  list;
    }
}
  • 定义ResultSeHandler< T > 接口
public interface ResultSeHandler<T> {
     
    List<T> handleResultSet(ResultSet resultSet,Class<T> acl);
}
  • ResultSetHandlerImpl< T >实现类
public class ResultSetHandlerImpl<T> implements ResultSeHandler<T> {
     
    @Override
    public List<T> handleResultSet(ResultSet resultSet, Class<T> acl) {
     
        List<T> list =new ArrayList<>();

        try {
     
            BeanInfo beanInfo = Introspector.getBeanInfo(acl, Object.class);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            while (resultSet.next()) {
     
                T t =acl.newInstance();
                for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
     
                    //获取对象中的属性
                    String name = propertyDescriptor.getName();
                    //获取数据库中的列名
                    Object value = resultSet.getObject(name);
                  //  System.out.println(value);
                    //获取set方法
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    //执行
                    writeMethod.invoke(t,value);

                }
                //装进集合
                list.add(t);
            }
        } catch (Exception e) {
     
            e.printStackTrace();
        }
        return list;
    }
}


- domain包

  • 定义Teacher类
@Data
public class Teacher {
     
    private int id;
    private String name;
    private Integer age;
    private String email;
}


- dao包

  • 定义接口ITeacherDAO

    public interface ITeacherDAO {
           
        //添加
        void insert(Teacher teacher);
        //删除
        void delete(int id);
        //改
        void update(Teacher teacher);
        //查询指定id
        Teacher select(int id);
        //查询所有
        List<Teacher> selectAll();
    }
    
    • impl包
    • 定义TeacherDAOImpl实现类
    public class TeacherDAOImpl implements ITeacherDAO {
           
        //插入
        @Override
        public void insert(Teacher teacher) {
           
            String sql = "insert into t_teacher (name,age,email) values(?,?,?)";  
            DQLUtil.executeUpdate(sql,teacher.getName(),teacher.getAge(),teacher.getEmail());
        }
        //删除
        @Override
        public void delete(int id) {
           
            String sql ="delete from t_teacher where id =?";
            DQLUtil.executeUpdate(sql,id);
        }
        //更新
        @Override
        public void update(Teacher teacher) {
           
            String sql ="update t_teacher set name=?,age=?,email=? where id=?";
        DQLUtil.executeUpdate(sql,teacher.getName(),teacher.getAge(),teacher.getEmail(),teacher.getId());
    
        }
        //查询 DQL
        @Override
        public Teacher select(int id) {
              
            String sql = "select * from t_teacher where id=?";
            List<Teacher> list = DQLUtil.executeQuery(sql, new ResultSetHandlerImpl(), Teacher.class, id);
             if (list!=null &&list.size() >0){
           
                 return  list.get(0);
             }
    
            return null;
        }
       //查询整个集合
        @Override
        public List<Teacher> selectAll() {
              
            String sql = "select*from t_teacher";
            List<Teacher> list1 = DQLUtil.executeQuery(sql, new ResultSetHandlerImpl(), Teacher.class);
    
            return list1;
        }
    }
    
    


- test包

  • 测试类
public class TeacherDAOImplTest {
     
    private static ITeacherDAO teacherDAO =new TeacherDAOImpl();
       //插入
    @Test
    public void insert() {
     
        Teacher teacher = new Teacher();
        teacher.setName("月月");
        teacher.setAge(12);
        teacher.setEmail("我爱小轩");
        teacherDAO.insert(teacher);

    }
    //删除
    @Test
    public void delete() {
     
        teacherDAO.delete(5);
    }
    //更新
    @Test
    public void update() {
     
       Teacher teacher =new Teacher();
       teacher.setName("臭猪");
       teacher.setAge(18);
       teacher.setEmail("fewgw");
       teacher.setId(6);
       teacherDAO.update(teacher);
    }
  //查询指定id
    @Test
    public void select() {
     
        Teacher select = teacherDAO.select(1);
        System.out.println(select);
    }
  //查询集合
    @Test
    public void selectAll() {
     
        List<Teacher> list = teacherDAO.selectAll();
        System.out.println(list);
    }
}


JDBC事务操作

事务(Transaction ,简称tx)

让多个操作,捆绑在一起,多个操作中有一个失败,那么整个操作失败


在数据库中,事务是指一组逻辑操作单元,使数据从一种状态换成另外一种状态,保证了操作要么同时成功,要么同时失败


事务的ACID属性

  • 原子性(Atomicity):指事务是一个不可分割的工作单位
  • 一致性(Consistency):包装数据的完整性,事务必须使数据库从一个一致性状态变换到另外一个状态
  • 隔离性(Isolation):指一个事务的执行不能被其他事务干扰
  • 持久性(Durability):一旦一个事务被提交,它的数据库中数据的改变是永久性的

对应方法只要事务开启了手动执行, 连接要么提交,要么回滚 不能省略


连接池

javax.sql.DataSource 接口

用来存放多个连接对象,避免了资源浪费

基本四要素:driverClassName,url,username,password

其他属性:对连接对象被限制的配置

  • 初始化连接
  • 最多连接数
  • 最少连接数
  • 最长等待时间
  • 最长超过时间

常见的DataSource实现

  • DBCP:Spring框架推荐
  • C3P0:Hibernate框架(基本不用了)
  • druid :阿里巴巴连接池


使用DBCP连接池

DBCP与druid步骤基本一致

基本写法

首先导入相关数据池的jar包

//DBCP数据库连接池 ,改造前
public class DBCPDemo {
     
    public static void main(String[] args) throws SQLException {
     
         DataSource dataSource=setupDataSource();
        Connection connection=dataSource.getConnection();
        System.out.println("connection = " + connection);
    }

    public static DataSource setupDataSource() {
     
        BasicDataSource ds = new BasicDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUsername("root");
        ds.setPassword("123");
        ds.setUrl("jdbc:mysql:///product");
        return ds;
    }
}

但是这样去使用连接池,上述代码就会产生了硬编码,我们应该对其进行优化,即主流写法

首先使用DBCP连接池时,对应的数据库信息应该从properties配置文件中读取,使用连接池首先需要创建DataSource对象,其中DBCP已经提供创建该对象的工厂方法BasicDataSourceFactory.createDataSource(Properties properties),并且可以传入properties对象并读取该配置文件的内容,那么步骤就简化很多了

  • 创建db.properties
 //注意名字是有指定写法的,不能随意修改
driverClassName=com.mysql.jdbc.Driver
username=root
password=123
url=jdbc:mysql:///product

  • 创建工具类 DBCPUtil
public final class DBCPUtil {
     
    private DBCPUtil() {
     
    }

    private static DataSource ds = null;
     //使用静态代码块,加载配置文件资源
    static {
     
        try {
     
            InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            //加载进properties内存中
            properties.load(inputStream);
            //创建datasource对象,并传入properties对象
            ds = BasicDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
     
            e.printStackTrace();
        }

    }
        //写一个工具方法 ,直接调用
    public static DataSource getDataSource() {
     
        return ds;
    }

    //为了更简便 ,直接把连接对象步骤写进该方法
    public static Connection getConnection() {
     

        try {
     
            return ds.getConnection();
        } catch (SQLException e) {
     
            e.printStackTrace();
        }
        return null;
    }
}
  • 测试类
  public static void main(String[] args) throws SQLException {
     
        //直接使用工具类,调用即可,成功连接到数据库
         Connection connection =DBCPUtil.getConnection();
        System.out.println("connection = " + connection);

    }

你可能感兴趣的:(Java重修之巅峰之路,java,java,jdbc,连接池,数据库)