jdbc&连接池&DButils&分页

jdbc基本操作

jdbc操作顺序

导入驱动包,或者引入依赖

1.装载驱动

Class.forName("com.mysql.jdbc.Driver");

2.获取连接对象 connection

Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/zhongfucheng", "root", "1234");

3.获取执行SQL语句的对象 statement

Statement  statement = connection.createStatement();

4.执行SQL语句 得到结果集 resultSet

ResultSet  resultSet = statement.executeQuery("SELECT * FROM users");
//操作结果集
//遍历结果集,得到数据
            while (resultSet.next()) {

                System.out.println(resultSet.getString(1));

                System.out.println(resultSet.getString(2));
            }
#### 
    结果集的列 索引从1开始
  1. 关闭连接
resultSet.close();
statement.close();
connection.close();

JDBC改进

preparedStatment

PreparedStatement对象继承Statement对象,它比Statement对象更强大,使用起来更简单

  • PreparedStatement可以使用占位符,简化sql的编写
  • PreparedStatement可对SQL进行预编译,提高效率,预编译的SQL存储在PreparedStatement对象中
//模拟查询id为2的信息
        String id = "2";

        Connection connection = UtilsDemo.getConnection();

        String sql = "SELECT * FROM users WHERE id = ?";
        PreparedStatement preparedStatement = connection.preparedStatement(sql);

        //第一个参数表示第几个占位符【也就是?号】,第二个参数表示值是多少
        preparedStatement.setString(1,id);

        ResultSet resultSet = preparedStatement.executeQuery();

        if (resultSet.next()) {
            System.out.println(resultSet.getString("name"));
        }

        //释放资源
        UtilsDemo.release(connection, preparedStatement, resultSet);

元数据

元数据其实就是数据库,表,列的定义信息

通过元数据可以获得数据库表的列名

  • ParameterMetaData --参数的元数据
  • ResultSetMetaData --结果集的元数据
  • DataBaseMetaData --数据库的元数据
案例:将查询结果封装到bean对象中
//接口实现类,对结果集封装成一个Bean对象
public class BeanHandler implements ResultSetHandler {


    //要封装成一个Bean对象,首先要知道Bean是什么,这个也是调用者传递进来的。
    private Class clazz;

    public BeanHandler(Class clazz) {
        this.clazz = clazz;
    }

    @Override
    public Object hanlder(ResultSet resultSet) {

        try {

            //创建传进对象的实例化 反射
            Object bean = clazz.newInstance();

            if (resultSet.next()) {

                //拿到结果集元数据
                ResultSetMetaData resultSetMetaData = resultSet.getMetaData();

                for (int i = 0; i < resultSetMetaData.getColumnCount(); i++) {

                    //获取到每列的列名 从1开始
                    String columnName = resultSetMetaData.getColumnName(i+1);

                    //获取到每列的数据 从1开始
                    String columnData = resultSet.getString(i+1);

                    //设置Bean属性
                    Field field = clazz.getDeclaredField(columnName);
                    field.setAccessible(true);
                    field.set(bean,columnData);
                }

                //返回Bean对象
                return bean;
            }

连接池

自己编写连接池
  • 实现java.sql.DataSource接口

  • 创建容器用来保存Connection【此容器选择LinkedList】

  • 静态代码块中读取连接信息

    static {
            //读取文件配置
            InputStream inputStream = Demo1.class.getClassLoader().getResourceAsStream("db.properties");
    
            Properties properties = new Properties();
            try {
                properties.load(inputStream);
                String url = properties.getProperty("url");
                String username = properties.getProperty("username");
                String driver = properties.getProperty("driver");
                String password = properties.getProperty("password");
    
                //加载驱动
                Class.forName(driver);
    
                //获取多个连接,保存在LinkedList集合中
                for (int i = 0; i < 10; i++) {
                    Connection connection = DriverManager.getConnection(url, username, password);
                    list.add(connection);
                }
                
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
    
        }
    
  • 实现DataSource中的getConnection()方法

  • 在getConnection()中返回一个动态代理对象,用来改写close()方法

     @Override
        public Connection getConnection() throws SQLException {
    
            if (list.size() > 0) {
                final Connection connection = list.removeFirst();
    
                //看看池的大小
                System.out.println(list.size());
    
                //返回一个动态代理对象
                return (Connection) Proxy.newProxyInstance(Demo1.class.getClassLoader(), connection.getClass().getInterfaces(), new InvocationHandler() {
    
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
                        //如果不是调用close方法,就按照正常的来调用
                        if (!method.getName().equals("close")) {
                            return method.invoke(connection, args);
                        } else {
    
                            //进到这里来,说明调用的是close方法
                            list.add(connection);
    
                            //再看看池的大小
                            System.out.println(list.size());
    
                        }
                        return null;
                    }
    
                });
            }
            return null;
        }
    
    
连接与线程绑定
/*
* 连接工具类,用于从数据源中获取连接,并且实现和线程的绑定
* */
@Component("connectionUtils")
public class ConnectionUtils {
    private ThreadLocal<Connection>  sql=new ThreadLocal<Connection>();

//此处连接池,使用c3p0
    @Autowired
    private DataSource datasource;
    public void setDatasource(DataSource datasource) {
        this.datasource = datasource;
    }
    //获取当前线程的连接
    public Connection getThreadConnection(){
        try {
            Connection con = sql.get();
            if (con == null) {
                //当前线程没有连接,从数据源中获取连接
                con = datasource.getConnection();
                //把con绑定线程ThreadLocal
                sql.set(con);
            }
            return con;
        }catch (Exception e){
            throw  new RuntimeException(e);
        }
    }
    public void removeConnection(){
        //线程和连接解绑,因为连接的close方法不是将连接删除而是还回线程池,所以将线程和连接解绑
        sql.remove();
    }
}

这样可以直接从线程中获取连接

#ThreadLocal的作用:
    简要言之:往ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal能够实现当前线程的操作都是用同一个Connection,保证了事务!

连接池C3p0的使用

以下为在spring框架中的使用

  1. 导入依赖

    <dependency>
                <groupId>c3p0</groupId>
                <artifactId>c3p0</artifactId>
                <version>0.9.1.2</version>
     </dependency>
    
  2. 配置连接池

      <bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <!--jdbc:mysql://localhost:3306/数据库名-->
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
            <property name="user" value="root"></property>
            <property name="password" value="1234"></property>
        </bean>
    
  3. 注入datasource,获取连接

    @Autowired
        private DataSource datasource;
    Connetion con = datasource.getConnection();
    

Dbutils

dbutils它是对JDBC的简单封装,极大简化jdbc编码的工作量

一般配合c3p0使用

DbUtils类

用来连接的工具类,一般不使用,用c3p0代替

QueryRunner类

该类简化了SQL查询,配合ResultSetHandler使用,可以完成大部分的数据库操作,重载了许多的查询,更新,批处理方法。大大减少了代码量

ResultSetHandler接口

该接口规范了对ResultSet的操作,要对结果集进行什么操作,传入ResultSetHandler接口的实现类即可。

  • ArrayHandler:把结果集中的第一行数据转成对象数组。
  • ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
  • BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
  • BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
  • ColumnListHandler:将结果集中某一列的数据存放到List中。
  • KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
  • MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
  • MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
  • ScalarHandler 将ResultSet的一个列到一个对象中。
  @org.junit.Test
    public void query()throws SQLException {

        //创建出QueryRunner对象
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "SELECT * FROM student";

        List list = (List) queryRunner.query(sql, new BeanListHandler(Student.class));
        System.out.println(list.size());

    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OjU3BEqk-1607430115389)(C:\Users\hello\AppData\Roaming\Typora\typora-user-images\image-20201109230708560.png)]

c3p0配置之后的bean就是一个DateSource对象

或者:

将connection对象传入

runner.query(connectionUtils.getThreadConnection(),"select* from account ", new BeanListHandler<Account>(Account.class));

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nVDt6vCB-1607430115392)(C:\Users\hello\AppData\Roaming\Typora\typora-user-images\image-20201109230533873.png)]

具体使用过程请看day5项目

Mysql分页

mysql查询分页的语法:

 /*
      Mysql分页语法:
      @start---偏移量,不设置就是从0开始【也就是(currentPage-1)*lineSize】
      @length---长度,取多少行数据
    
    */
    SELECT *
    FROM 表名
    LIMIT [START], length;
分页查询需要的参数
  • currentPage 为当前页【由用户决定的】
  • lineSize 每页的数据条数【由我们开发人员决定
  • totalRecord–总数据数【查询表可知】
  • pageCount–页数【totalRecord和lineSize决定】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vbgD6N0j-1607430115395)(C:\Users\hello\AppData\Roaming\Typora\typora-user-images\image-20201109231910913.png)]

配合图片查看我们的需求:

  • 算出多少页数据

    用totalRecord除以lineSize 
    
  • 根据页码,从数据库中查出相应数据

    传入currentPage 和lineSize 
    

Mysql分页具体代码:(原生方式)

 //每页显示3条数据
        int lineSize = 3;

        //总记录数
        int totalRecord = getTotalRecord();

        //假设用户指定的是第2页
        int currentPage = 2;

        //一共有多少页
        int pageCount = getPageCount(totalRecord, lineSize);

        //使用什么数据库进行分页,记得要在JdbcUtils中改配置
        List<Person> list = getPageData2(currentPage, lineSize);
        for (Person person : list) {
            System.out.println(person);
        }

    }

    //使用JDBC连接Mysql数据库实现分页
    public static List<Person> getPageData(int currentPage, int lineSize) throws SQLException {

        //从哪个位置开始取数据
        int start = (currentPage - 1) * lineSize;

        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "SELECT name,address  FROM person LIMIT ?,?";

        List<Person> persons = (List<Person>) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{start, lineSize});
        return persons;

    }

    public static int getPageCount(int totalRecord, int lineSize) {

        //简单算法
        //return (totalRecord - 1) / lineSize + 1;

        //此算法比较好理解,把数据代代进去就知道了。
        return totalRecord % lineSize == 0 ? (totalRecord / lineSize) : (totalRecord / lineSize) + 1;

    }


    public static int  getTotalRecord() throws SQLException {

        //使用DbUtils框架查询数据库表中有多少条数据
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "SELECT COUNT(*) FROM person";

        Object o = queryRunner.query(sql, new ScalarHandler());

        String ss = o.toString();
        int  s = Integer.parseInt(ss);
        return s;
    }

Orcale中的分页:(使用Orcale支持的SQL语句)

  //使用JDBC连接Oracle数据库实现分页
    public static List<Person> getPageData2(int currentPage, int lineSize) throws SQLException {

        //从哪个位置开始取数据
        int start = (currentPage - 1) * lineSize;

        //读取前N条数据
        int end = currentPage * lineSize;

        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "SELECT " +
                "  name, " +
                "  address " +
                "FROM ( " +
                "  SELECT " +
                "    name, " +
                "    address , " +
                "    ROWNUM rn " +
                "  FROM person " +
                "  WHERE ROWNUM <= ? " +
                ")temp WHERE temp.rn>?";

        List<Person> persons = (List<Person>) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{end, start});
        return persons;

    }

            "  address " +
            "FROM ( " +
            "  SELECT " +
            "    name, " +
            "    address , " +
            "    ROWNUM rn " +
            "  FROM person " +
            "  WHERE ROWNUM <= ? " +
            ")temp WHERE temp.rn>?";

    List<Person> persons = (List<Person>) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{end, start});
    return persons;

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值