JDBC [database connection pool, DbUtils framework, paging]

1. Database connection pool

What is database connection pool

Simply put: the database connection pool is to provide connections. . .

Why do we use database connection pooling

  • The establishment and closing of database connections are very resource-intensive
  • Frequent opening and closing of connections causes poor system performance

write connection pool

  1. To write a connection pool, you need to implement the java.sql.DataSource interface
  2. Create a batch of Connections and save them with LinkedList [since it is a pool, of course, use a collection to save, the bottom layer of LinkedList is a linked list, which has better performance for additions and deletions]
  3. Implement getConnetion() , so that each call to getConnection() takes a Connection in LinkedList and returns it to the user
  4. Call Connection.close() method, Connction returns to LinkedList



    private static LinkedList<Connection> list = new LinkedList<>();
    
    //获取连接只需要一次就够了,所以用static代码块
    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();
        }

    }

    //重写Connection方法,用户获取连接应该从LinkedList中给他
    @Override
    public Connection getConnection() throws SQLException {
        System.out.println(list.size());
        System.out.println(list);

       //先判断LinkedList是否存在连接
       return list.size() > 0 ? list.removeFirst() : null; 
    }



We have completed the first three steps, and now the problem comes**. We call the Conncetion.close() method to close the physical connection to the database, not the ** returned to the LinkedList

Solutions:

  1. Write a Connection subclass that overrides the close() method
  2. Write a Connection wrapper class to enhance the close() method
  3. Use dynamic proxy, return a proxy object, intercept the call of the close() method, and enhance the close()

Analyze the first idea:

  • Connection is loaded by the database driver and saves the information of the data . Write a subclass Connection, new out the object, the connection of the subclass cannot directly inherit the data information of the parent class, that is to say, the Connection of the subclass cannot connect to the database , let alone override the close() method.

Analyze the second idea:

  • Write a Connection wrapper class.
    1. Write a class that implements the same interface as the enhanced object [Connection interface]
    2. Define a variable that points to the enhanced object
    3. Define a constructor to receive the enhanced object
    4. Override the method you want to enhance
    5. For methods that do not want to be enhanced, directly call the method of the enhanced object
  • There is nothing wrong with this idea itself, that is, when implementing an interface, there are too many methods! , so we don't use this method either

Analyze the third idea code implementation:


    @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")) {
                        method.invoke(connection, args);
                    } else {

                        //进到这里来,说明调用的是close方法
                        list.add(connection);

                        //再看看池的大小
                        System.out.println(list.size());

                    }
                    return null;
                }

            });
        }
        return null;
    }


We have been able to simply write a thread pool above. Let's use the open source database connection pool

DBCP

Steps to use DBCP data source:

  1. Import two jar packages [Commons-dbcp.jar and Commons-pool.jar]
  2. read configuration file
  3. Get the BasicDataSourceFactory object
  4. Create a DataSource object

    private static DataSource dataSource = null;

    static {
        try {
            //读取配置文件
            InputStream inputStream = Demo3.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
            Properties properties = new Properties();
            properties.load(inputStream);

            //获取工厂对象
            BasicDataSourceFactory basicDataSourceFactory = new BasicDataSourceFactory();
            dataSource = basicDataSourceFactory.createDataSource(properties);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();

    }

    //这里释放资源不是把数据库的物理连接释放了,是把连接归还给连接池【连接池的Connection内部自己做好了】
    public static void release(Connection conn, Statement st, ResultSet rs) {

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

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

        }
    }


C3P0

The performance of the C3P0 data source is even better, and it can use XML configuration files to configure the information!

step:

  1. Import the development packages [c3p0-0.9.2-pre1.jar] and [mchange-commons-0.2.jar]
  2. Import XML configuration files [you can configure them one by one in the program, there are examples of XML files in Configuration in the doc of C3P0]
  3. new out the ComboPooledDataSource object

    private static ComboPooledDataSource comboPooledDataSource = null;

    static {
        //如果我什么都不指定,就是使用XML默认的配置,这里我指定的是oracle的
        comboPooledDataSource = new ComboPooledDataSource("oracle");
    }

    public static Connection getConnection() throws SQLException {
        return comboPooledDataSource.getConnection();
    }

Tomcat data source

The Tomcat server also provides us with a connection pool, which is actually DBCP internally

step:

  1. Configure the context.xml file in the META-INF directory [the content of the file can be found in Configure Tomcat's Resource Factory under JNDI Resources on the default page of tomcat]
  2. Import the Mysql or oracle development package into the lib directory of tomcat
  3. Initialize JNDI->Get JNDI container->Retrieve the connection pool stored in the JNDI container with the name XXX

Configuration of the context.xml file:


<Context>

  <Resource name="jdbc/EmployeeDB"
            auth="Container"
            type="javax.sql.DataSource"
            
            username="root"
            password="root"
            driverClassName="com.mysql.jdbc.Driver"
            url="jdbc:mysql://localhost:3306/zhongfucheng"
            maxActive="8"
            maxIdle="4"/>
</Context>


        try {

			//初始化JNDI容器
            Context initCtx = new InitialContext();

			//获取到JNDI容器
            Context envCtx = (Context) initCtx.lookup("java:comp/env");

			//扫描以jdbc/EmployeeDB名字绑定在JNDI容器下的连接池
            DataSource ds = (DataSource)
                    envCtx.lookup("jdbc/EmployeeDB");

            Connection conn = ds.getConnection();
            System.out.println(conn);

        } 


Using the dbutils framework

dbutils is a simple encapsulation of JDBC, which greatly simplifies the workload of jdbc coding

DbUtils class

Provides tool classes for closing connections, loading JDBC drivers, rolling back and submitting transactions, etc. [It is rarely used, because we have learned connection pools, we should use connection pools to connect to databases]

QueryRunner class

This class simplifies SQL queries, and can be used with ResultSetHandler to complete most of the database operations , overloading many query, update, and batch methods. Greatly reduces the amount of code

ResultSetHandler interface

This interface regulates the operation of the ResultSet. To operate the result set, you can pass in the implementation class of the ResultSetHandler interface.

  • ArrayHandler: Convert the first row of data in the result set into an array of objects.
  • ArrayListHandler: Convert each row of data in the result set into an array and store it in a List.
  • BeanHandler: Encapsulates the first row of data in the result set into a corresponding JavaBean instance.
  • BeanListHandler: Encapsulates each row of data in the result set into a corresponding JavaBean instance and stores it in a List.
  • ColumnListHandler: Store the data of a column in the result set in the List.
  • KeyedHandler(name): Encapsulate each row of data in the result set into a Map, and then store these maps into a map whose key is the specified key.
  • MapHandler: Encapsulates the first row of data in the result set into a Map, the key is the column name, and the value is the corresponding value.
  • MapListHandler: Encapsulates each row of data in the result set into a Map, and then stores it in a List
  • ScalarHandler takes a column of a ResultSet into an object.

CRUD to database using DbUtils framework



/*
* 使用DbUtils框架对数据库的CRUD
* 批处理
*
* */
public class Test {

    @org.junit.Test
    public void add() throws SQLException {

        //创建出QueryRunner对象
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "INSERT INTO student (id,name) VALUES(?,?)";

        //我们发现query()方法有的需要传入Connection对象,有的不需要传入
        //区别:你传入Connection对象是需要你来销毁该Connection,你不传入,由程序帮你把Connection放回到连接池中
        queryRunner.update(sql, new Object[]{"100", "zhongfucheng"});

    }

    @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());

    }

    @org.junit.Test
    public void delete() throws SQLException {
        //创建出QueryRunner对象
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "DELETE FROM student WHERE id='100'";

        queryRunner.update(sql);
    }

    @org.junit.Test
    public void update() throws SQLException {
        //创建出QueryRunner对象
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "UPDATE student SET name=? WHERE id=?";

        queryRunner.update(sql, new Object[]{"zhongfuchengaaa", 1});
    }

    @org.junit.Test
    public void batch() throws SQLException {
        //创建出QueryRunner对象
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "INSERT INTO student (name,id) VALUES(?,?)";

        Object[][] objects = new Object[10][];
        for (int i = 0; i < 10; i++) {
            objects[i] = new Object[]{"aaa", i + 300};
        }
        queryRunner.batch(sql, objects);
    }

}

 

pagination

Paging technology is very common. Searching pages under a search engine cannot display all the data on one page . So we use paging technology.

Oracle implements paging


	/*
	  Oracle分页语法:
	    @lineSize---每页显示数据行数
	    @currentPage----当前所在页
	
	*/
	SELECT *FROM (
	    SELECT 列名,列名,ROWNUM rn
	    FROM 表名
	    WHERE ROWNUM<=(currentPage*lineSize)) temp
	
	WHERE temp.rn>(currentPage-1)*lineSize;


Simple explanation of Oracle paging principle :


	/*
	  Oracle分页:
	    Oracle的分页依赖于ROWNUM这个伪列,ROWNUM主要作用就是产生行号。
	
	  分页原理:
	    1:子查询查出前n行数据,ROWNUM产生前N行的行号
	    2:使用子查询产生ROWNUM的行号,通过外部的筛选出想要的数据
	
	  例子:
	    我现在规定每页显示5行数据【lineSize=5】,我要查询第2页的数据【currentPage=2】
	    注:【对照着语法来看】
	
	  实现:
	    1:子查询查出前10条数据【ROWNUM<=10】
	    2:外部筛选出后面5条数据【ROWNUM>5】
		3:这样我们就取到了后面5条的数据
	*/

Mysql implements paging


	/*
	  Mysql分页语法:
	  @start---偏移量,不设置就是从0开始【也就是(currentPage-1)*lineSize】
	  @length---长度,取多少行数据
	
	*/
	SELECT *
	FROM 表名
	LIMIT [START], length;
	
	/*
	  例子:
	    我现在规定每页显示5行数据,我要查询第2页的数据
	
	  分析:
	    1:第2页的数据其实就是从第6条数据开始,取5条
	
	  实现:
	    1:start为5【偏移量从0开始】
	    2:length为5

*/

Summarize:

  • Mysql fetches data from (currentPage-1)*lineSize, and fetches lineSize pieces of data
  • Oracle first obtains currentPage*lineSize pieces of data, and starts to fetch data from (currentPage-1)*lineSize

Use JDBC to connect to the database to achieve paging

The following are common pagination pictures

 

 

With the pictures, let's see what our needs are:

  1. Figure out how many pages of data to display on the page
  2. According to the page number, the corresponding data is displayed from the database.

analyze:

  1. It is very simple to calculate how many pages of data there are [query how many records there are in the database, and how many records you display per page, you can calculate how many pages of data there are]
  2. Use Mysql or Oracle's paging syntax

Through the above analysis, we will find that we need to use 4 variables

  • currentPage--the current page [determined by the user]
  • totalRecord--the total number of data [query table can be seen]
  • lineSize-- the number of displayed data per page [determined by our developers]
  • pageCount--the number of pages [totalRecord and lineSize decide]

        //每页显示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;

    }

    //使用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;

    }

    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;
    }


If there are any mistakes in the article, please correct me, and we can communicate with each other. Students who are used to reading technical articles on WeChat can pay attention to the WeChat public account: Java3y.

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324386859&siteId=291194637