J2EE进阶之JDBC分页,大文本数据存储,批处理,事物处理 十八

来源:互联网 发布:java怎么安装 编辑:程序博客网 时间:2024/05/21 07:06

分页,批处理,大文本数据存储,事物处理

分页

1:MySQL数据库的支持:

Select * from table limit M,N
  **M:记录开始索引位置
  N:取多少条记录。**

需求:

每页显示10条记录:
第一页:SELECT * FROM customers LIMIT 0,10
第二页:SELECT * FROM customers LIMIT 10,10
第三页:SELECT * FROM customers LIMIT 20,10
第x页:SELECT * FROM customers LIMIT (x-1)*10,10

总共多少页:
总条数%10==0?总条数/10:总条数/10+1;

以上一个项目对表单的操作为例:

2:设计一个类,封装了所有与分页有关的信息

完成WEB页面的分页显示

先获得需分页显示的记录总数,然后在web页面中显示页码。
根据页码,从数据库中查询相应的记录显示在web页面中。

以上两项操作通常使用Page对象进行封装。

page代码:

  1 package jdbcdemo2.com.wsj.commons;  2   3 import java.util.List;  4   5 /**  6  * 封装所有与分页有关的信息  7  * @author Angus  8  *  9  */ 10 public class Page { 11     private List records;//要显示的分页数据 12     private int currentPageNum = 1;//当前页码。默认应该为1 13     private int totalPageCount;//总页数:可以计算 14     private int totalRecordsNum;//总记录条数 15     private int pageSize = 10;//每页显示的条数 16     private int startIndex;//每页开始记录的索引:可以计算 17     private int prePageNum;//上一页的页码 18     private int nextPageNum;//下一页的页码 19      20     private String uri;//查询分页的地址路径 21     //------------------------- 22     private int startPage;//页码:开始页码 23     private int endPage;//页码:结束页码 24     //------------------------- 25     public Page(int currentPageNum,int totalRecordsNum){ 26         this.currentPageNum = currentPageNum; 27         this.totalRecordsNum = totalRecordsNum; 28         //计算总页数 29         totalPageCount = totalRecordsNum%pageSize==0?totalRecordsNum/pageSize:totalRecordsNum/pageSize+1; 30         //计算每页开始记录的索引 31         startIndex = (currentPageNum-1)*pageSize; 32          33         //计算开始页码和结束页码 34         if(totalPageCount<=9){ 35             startPage = 1; 36             endPage = totalPageCount; 37         }else{ 38             //总页数大于9 39             startPage = currentPageNum-4; 40             endPage = currentPageNum+4; 41             if(startPage<1){ 42                 startPage = 1; 43                 endPage = 9; 44             } 45             if(endPage>totalPageCount){ 46                 endPage = totalPageCount; 47                 startPage = endPage-8; 48             } 49         } 50          51          52     } 53     public int getPrePageNum() { 54         prePageNum = currentPageNum-1; 55         if(prePageNum<1) 56             prePageNum = 1; 57         return prePageNum; 58     } 59     public int getNextPageNum() { 60         nextPageNum = currentPageNum+1; 61         if(nextPageNum>totalPageCount) 62             nextPageNum = totalPageCount; 63         return nextPageNum; 64     } 65  66     public void setPrePageNum(int prePageNum) { 67         this.prePageNum = prePageNum; 68     } 69  70     public List getRecords() { 71         return records; 72     } 73  74     public void setRecords(List records) { 75         this.records = records; 76     } 77  78     public int getCurrentPageNum() { 79         return currentPageNum; 80     } 81  82     public void setCurrentPageNum(int currentPageNum) { 83         this.currentPageNum = currentPageNum; 84     } 85  86     public int getTotalPageCount() { 87         return totalPageCount; 88     } 89  90     public void setTotalPageCount(int totalPageCount) { 91         this.totalPageCount = totalPageCount; 92     } 93  94     public int getTotalRecordsNum() { 95         return totalRecordsNum; 96     } 97  98     public void setTotalRecordsNum(int totalRecordsNum) { 99         this.totalRecordsNum = totalRecordsNum;100     }101 102     103 104     public void setNextPageNum(int nextPageNum) {105         this.nextPageNum = nextPageNum;106     }107 108     public int getPageSize() {109         return pageSize;110     }111 112     public void setPageSize(int pageSize) {113         this.pageSize = pageSize;114     }115 116     public int getStartIndex() {117         return startIndex;118     }119 120     public void setStartIndex(int startIndex) {121         this.startIndex = startIndex;122     }123     public int getStartPage() {124         return startPage;125     }126     public void setStartPage(int startPage) {127         this.startPage = startPage;128     }129     public int getEndPage() {130         return endPage;131     }132     public void setEndPage(int endPage) {133         this.endPage = endPage;134     }135     public String getUri() {136         return uri;137     }138     public void setUri(String uri) {139         this.uri = uri;140     }141     142 }

3:改造Service接口

BusinessService接口

/** * 根据用户要看的页码,查询封装了所有分页信息有关的Page对象 * @param pageNum 如果为null,则默认值是1 * @return */Page findPageRecords(String pageNum);

BusinessServiceImpl实现

@Overridepublic Page findPageRecords(String pageNum) {    int currentPageNum = 1;    if(pageNum!=null){        currentPageNum = Integer.parseInt(pageNum);    }    int totalRecordsNum = dao.findTotalRecordsNum();// 查询记录的总条数    Page page = new Page(currentPageNum, totalRecordsNum);    //把记录搞到page中    List<Customer> records = dao.findPageRecords(page.getStartIndex(),page.getPageSize());//查询分页记录    page.setRecords(records);    return page;}

4:改造DAO层

CustomerDao接口

/** * 查询记录总条数 * @return */int findTotalRecordsNum();/** * 根据索引和大小查询分页记录 * @param startIndex * @param pageSize * @return */List<Customer> findPageRecords(int startIndex, int pageSize);

CustomerDaoMySQLImpl实现

@Overridepublic int findTotalRecordsNum() {    try{        conn = JDBCUtils.getConnection();        stmt = conn.prepareStatement("select count(*) from customers");        rs = stmt.executeQuery();        if(rs.next()){            return rs.getInt(1);        }        return 0;    }catch(Exception e){        throw new RuntimeException(e);    }finally{        JDBCUtils.release(rs, stmt, conn);    }}@Overridepublic List<Customer> findPageRecords(int startIndex, int pageSize) {    try{        conn = JDBCUtils.getConnection();        stmt = conn.prepareStatement("select * from customers limit ?,?");        stmt.setInt(1, startIndex);        stmt.setInt(2, pageSize);        List<Customer> cs = new ArrayList<Customer>();        rs = stmt.executeQuery();        while(rs.next()){            Customer c = new Customer();            c.setId(rs.getString("id"));            c.setName(rs.getString("name"));            c.setGender(rs.getString("gender"));            c.setBirthday(rs.getDate("birthday"));            c.setPhonenum(rs.getString("phonenum"));            c.setEmail(rs.getString("email"));            c.setHobby(rs.getString("hobby"));            c.setType(rs.getString("type"));            c.setDescription(rs.getString("description"));            cs.add(c);        }        return cs;    }catch(Exception e){        throw new RuntimeException(e);    }finally{        JDBCUtils.release(rs, stmt, conn);    }}

5;改造Servlet

// 查询所有客户信息private void showAllCustomers(HttpServletRequest request,        HttpServletResponse response) throws ServletException, IOException {    // 获取用户要看的页面:参数num    String num = request.getParameter("num");    Page page = s.findPageRecords(num);    page.setUri("/CentrolServlet?op=showAllCustomers");    // 封装数据到域对象中    request.setAttribute("page", page);    request.getRequestDispatcher("/listCustomers.jsp").forward(request,            response);} 

6、改造页面

  1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>  2 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>  3 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  4 <html>  5   <head>  6     <title>显示所有客户信息</title>  7       8     <meta http-equiv="pragma" content="no-cache">  9     <meta http-equiv="cache-control" content="no-cache"> 10     <meta http-equiv="expires" content="0"> 11     <!-- 12     <link rel="stylesheet" type="text/css" href="styles.css"> 13     --> 14     <style type="text/css"> 15         .odd{ 16             background-color: #c3f3c3; 17         } 18         .even{ 19             background-color: #f3c3f3; 20         } 21     </style> 22   </head> 23    24   <body> 25     <table width="88%"> 26         <tr> 27             <td> 28                 <a href="${pageContext.request.contextPath}/addCustomer.jsp">添加</a> 29                 <a href="javascript:delMulti()">删除</a> 30             </td> 31         </tr> 32         <tr> 33             <td> 34             <form action="${pageContext.request.contextPath}/ControlServlet?op=delMultiCustomer" method="post"></form> 35                 <table border="1" width="100%"> 36                     <tr> 37                         <th>选择</th> 38                         <th>姓名</th> 39                         <th>性别</th> 40                         <th>生日</th> 41                         <th>手机</th> 42                         <th>邮件</th> 43                         <th>爱好</th> 44                         <th>类型</th> 45                         <th>备注</th> 46                         <th>操作</th> 47                     </tr> 48                     <!-- 从page.records 取分页数据 --> 49                     <c:forEach items="${page.records}" var="c" varStatus="vs"> 50                         <tr class="${vs.index%2==0?'odd':'even' }"> 51                             <td> 52                                 <input type="checkbox" name="ids" value="${c.id}"> 53                             </td> 54                             <td>${c.name}</td> 55                             <td>${c.gender=='male'?'男':'女' }</td> 56                             <td>${c.birthday}</td> 57                             <td>${c.phonenum}</td> 58                             <td>${c.email}</td> 59                             <td>${c.hobby}</td> 60                             <td>${c.type}</td> 61                             <td>${c.description}</td> 62                             <td> 63                                 [<a href="${pageContext.request.contextPath}/ControlServlet?op=editCustomerUI&customerId=${c.id}">修改</a>] 64                                 [<a href="javascript:delOne('${c.id}')">删除</a>] 65                             </td> 66                         </tr> 67                     </c:forEach> 68                 </table> 69             </td> 70         </tr> 71          72         <tr> 73             <td> 74                 <!-- 分页开始 --> 75                  76                 第${page.currentPageNum}页/共${page.totalPageCount}页&nbsp;&nbsp; 77                 <a href="${pageContext.request.contextPath}">首页</a>&nbsp;&nbsp; 78                 <a href="${pageContext.request.contextPath}/ControlServlet?op=showAllCustomers&num=  ${page.prePageNum}">上一页</a>&nbsp;&nbsp; 79                 <a href="${pageContext.request.contextPath}/ControlServlet?op=showAllCustomers&num=${page.nextPageNum}">下一页</a>&nbsp;&nbsp; 80                 <a href="${pageContext.request.contextPath}/ControlServlet?op=showAllCustomers&num=${page.totalPageCount}">尾页</a>&nbsp;&nbsp; 81              82             </td> 83          84         </tr> 85     </table> 86      87     <script type="text/javascript"> 88     function delOne(customerId){ 89         var sure = window.confirm("确定要删除吗?"); 90         if(sure){ 91             window.location.href="${pageContext.request.contextPath}/ControlServlet?op=delOneCustomer&customerId="+customerId; 92         } 93     } 94     function delMulti(){ 95         //得到所有的name=ids的对象 96         var idsObj = document.getElementsByName("ids"); 97         //判断用户有没有选择 98         var selected = false; 99         for(var i=0;i<idsObj.length;i++){100             if(idsObj[i].checked){101                 selected = true;102                 break;103             }104         }105         //没有选择的提示106         if(!selected){107             alert("笨蛋,删除前请先选择");108             return;109         }110         //用户选择了111         var sure = window.confirm("确定要删除所选记录吗?");112         if(sure){113             document.forms[0].submit();114         }115     }116     117     </script>118   </body>119 </html>

效果图:

6.2:页面选择跳转

实现代码:

<select onchange="jupm(this)">                <c:forEach begin="1" end="${page.totalPageCount}" var = "num">                    <option value = "${num }" ${page.currentPageNum==num?'selected="selected"':'' }>${num} </option>                </c:forEach></select><script type="text/javascript">                function jupm(selectObj){                    //alert(selectObj.value);                     var pageNum = selectObj.value;                    window.location.href="${pageContext.request.contextPath}/ControlServlet?op=showAllCustomers&num="+pageNum;                }</script>

6.3输入任意跳转

代码实现

<input id="num" name="num" value="" size="3"/><input type="button" value="跳转" onclick="jumpPage()"/>function jumpPage(){    var num = document.getElementById("num").value;    //alert(num);    window.location.href="${pageContext.request.contextPath}/ControlServlet?op=showAllCustomers&num="+num;}

6.4 模仿百度搜索页数

<c:forEach begin="${page.startPage }" end="${page.endPage }" var="num">            <a href="${pageContext.request.contextPath}/ControlServlet?op=showAllCustomers&num=${num}">${num}</a></c:forEach>

page代码

//-------------------------private int startPage;//页码:开始页码private int endPage;//页码:结束页码//-------------------------public Page(int currentPageNum,int totalRecordsNum){    this.currentPageNum = currentPageNum;    this.totalRecordsNum = totalRecordsNum;    //计算总页数    totalPageCount = totalRecordsNum%pageSize==0?totalRecordsNum/pageSize:totalRecordsNum/pageSize+1;    //计算每页开始记录的索引    startIndex = (currentPageNum-1)*pageSize;    //计算开始页码和结束页码    if(totalPageCount<=9){        startPage = 1;        endPage = totalPageCount;    }else{        //总页数大于9        startPage = currentPageNum-4;        endPage = currentPageNum+4;        if(startPage<1){            startPage = 1;            endPage = 9;        }        if(endPage>totalPageCount){            endPage = totalPageCount;            startPage = endPage-8;        }    }}

批处理

业务场景:当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。

实现批处理有两种方式,第一种方式:

  Statement.addBatch(sql) list
执行批处理SQL语句
  executeBatch()方法:执行批处理命令
  clearBatch()方法:清除批处理命令

public void test1(){    Connection conn = null;    Statement stmt = null;    try{        conn = JdbcUtil.getConnection();        stmt = conn.createStatement();        String sql1 = "insert into t1(id,name) values(1,'aa1')";        String sql2 = "insert into t1(id,name) values(2,'aa2')";        String sql3 = "delete from t1 where id=1";        //stmt对象中有一个List,这就sql语句的缓存。        stmt.addBatch(sql1);        stmt.addBatch(sql2);        stmt.addBatch(sql3);        int ii[] = stmt.executeBatch();//真正执行.返回的值是每条语句影响到的行数的数组        for(int i:ii)            System.out.println(i);    }catch(Exception e){        throw new RuntimeException(e);    }finally{        JdbcUtil.release(null, stmt, conn);    }}

总结:采用Statement.addBatch(sql)方式实现批处理:
**优点:可以向数据库发送多条不同的SQL语句。
缺点:
SQL语句没有预编译。
当向数据库发送多条语句相同,但仅参数不同的SQL语句时,需重复写上很多条SQL语句**。例如:

    Insert into user(name,password) values(‘aa’,’111’);    Insert into user(name,password) values(‘bb’,’222’);    Insert into user(name,password) values(‘cc’,’333’);    Insert into user(name,password) values(‘dd’,’444’);

实现批处理的第二种方式:

PreparedStatement.addBatch()
注意内存溢出问题

//想t1中插入10条记录:语句不变,参数不同而已。而PreparedStatement就代表一条语句。@Testpublic void test2(){    Connection conn = null;    PreparedStatement stmt = null;    try{        conn = JdbcUtil.getConnection();        stmt = conn.prepareStatement("insert into t1(id,name) values(?,?)");        //批量设置参数        for(int i=0;i<10;i++){            stmt.setInt(1, i+1);            stmt.setString(2, "aaa"+(i+1));            stmt.addBatch();        }        int ii[] = stmt.executeBatch();//真正执行.返回的值是每条语句影响到的行数的数组        for(int i:ii)            System.out.println(i);    }catch(Exception e){        throw new RuntimeException(e);    }finally{        JdbcUtil.release(null, stmt, conn);    }}

//批量插入1001条记录@Testpublic void test3(){    Connection conn = null;    PreparedStatement stmt = null;    try{        conn = JdbcUtil.getConnection();        stmt = conn.prepareStatement("insert into t1(id,name) values(?,?)");        //批量设置参数        for(int i=0;i<1001;i++){            stmt.setInt(1, i+1);            stmt.setString(2, "aaa"+(i+1));            stmt.addBatch();            //每100条就让数据库执行一次            if(i%100==0){                stmt.executeBatch();//执行                stmt.clearBatch();//清空已经在缓存中的参数            }        }        stmt.executeBatch();//执行    }catch(Exception e){        throw new RuntimeException(e);    }finally{        JdbcUtil.release(null, stmt, conn);    }}

总结:
采用PreparedStatement.addBatch()实现批处理
优点:发送的是预编译后的SQL语句,执行效率高。
缺点:只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据

大文本和二进制数据存储(了解)

在实际开发中,程序一般不需要把大文本或二进制数据保存到数据库。

基本概念:大数据也称之为LOB(Large Objects),LOB又分为:
clob和blob
 clob用于存储大文本。
 blob用于存储二进制数据,例如图像、声音、二进制文等。

对MySQL而言只有blob,而没有clob,mysql存储大文本采用的是Text,Text和blob分别又分为:
 TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT
 TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB

ClobDemo

  1 public class ClobDemo {  2     @Test  3     public void write(){  4         Connection conn = null;  5         PreparedStatement stmt = null;  6         ResultSet rs = null;  7         try{  8             conn = JdbcUtil.getConnection();  9             stmt = conn.prepareStatement("insert into t2 (id,content) values(?,?)"); 10             stmt.setInt(1, 1); 11             //用流的形式 12             File file = new File("src/海底捞.txt"); 13             Reader reader = new FileReader(file); 14             stmt.setCharacterStream(2, reader, (int)file.length());//JDK6.0 setCharacterStream(int index,Reader r,long lengh)MySQL驱动没有实现 15             stmt.executeUpdate(); 16         }catch(Exception e){ 17             e.printStackTrace(); 18         }finally{ 19             JdbcUtil.release(rs, stmt, conn); 20         } 21     } 22     @Test 23     public void read(){ 24         Connection conn = null; 25         PreparedStatement stmt = null; 26         ResultSet rs = null; 27         try{ 28             conn = JdbcUtil.getConnection(); 29             stmt = conn.prepareStatement("select * from t2 where id=1"); 30             rs = stmt.executeQuery(); 31             if(rs.next()){ 32                 Reader reader = rs.getCharacterStream("content"); 33                 //存到D盘上 34                 Writer writer = new FileWriter("d:/hdl.txt"); 35                 char buf[] = new char[1024]; 36                 int len = -1; 37                 while((len=reader.read(buf))!=-1){ 38                     writer.write(buf, 0, len); 39                 } 40                 reader.close(); 41                 writer.close(); 42             } 43         }catch(Exception e){ 44             e.printStackTrace(); 45         }finally{ 46             JdbcUtil.release(rs, stmt, conn); 47         } 48     } 49 }

BlobDemo

  1 public class BlobDemo {  2     @Test  3     public void write(){  4         Connection conn = null;  5         PreparedStatement stmt = null;  6         ResultSet rs = null;  7         try{  8             conn = JdbcUtil.getConnection();  9             stmt = conn.prepareStatement("insert into t3 (id,content) values(?,?)"); 10             stmt.setInt(1, 1); 11             //用流的形式 12             InputStream in = new FileInputStream("src/19.jpg"); 13             stmt.setBinaryStream(2, in, in.available());//JDK6.0 setCharacterStream(int index,Reader r,long lengh)MySQL驱动没有实现 14             stmt.executeUpdate(); 15         }catch(Exception e){ 16             e.printStackTrace(); 17         }finally{ 18             JdbcUtil.release(rs, stmt, conn); 19         } 20     } 21     @Test 22     public void read(){ 23         Connection conn = null; 24         PreparedStatement stmt = null; 25         ResultSet rs = null; 26         try{ 27             conn = JdbcUtil.getConnection(); 28             stmt = conn.prepareStatement("select * from t3 where id=1"); 29             rs = stmt.executeQuery(); 30             if(rs.next()){ 31                 InputStream in = rs.getBinaryStream("content"); 32                 //存到D盘上 33                 OutputStream out = new FileOutputStream("d:/jj.jpg"); 34                 byte buf[] = new byte[1024]; 35                 int len = -1; 36                 while((len=in.read(buf))!=-1){ 37                     out.write(buf, 0, len); 38                 } 39                 in.close(); 40                 out.close(); 41             } 42         }catch(Exception e){ 43             e.printStackTrace(); 44         }finally{ 45             JdbcUtil.release(rs, stmt, conn); 46         } 47     } 48 }

事物

事务的概念

事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。
例如:A——B转帐,对应于如下两条sql语句
 update account set money=money-100 where name=‘a’;
 update account set money=money+100 where name=‘b’;
数据库开启事务命令DTL

  • -start transaction 开启事务
  • -Rollback 回滚事务
  • -Commit 提交事务

      1 public class TransactionDemo {  2     //模拟转账  3     @Test  4     public void test1(){  5         Connection conn = null;  6         PreparedStatement stmt = null;  7         ResultSet rs = null;  8         try{  9             conn = JdbcUtil.getConnection(); 10             conn.setAutoCommit(false);//相当于开启事务:start transaction 11             stmt = conn.prepareStatement("update account set money=money-100 where name='bbb'"); 12             stmt.executeUpdate(); 13 //          int i=1/0; 14             stmt = conn.prepareStatement("update account set money=money+100 where name='aaa'"); 15             stmt.executeUpdate(); 16             conn.commit(); 17         }catch(Exception e){ 18             if(conn!=null){ 19                 try { 20                     conn.rollback(); 21                 } catch (SQLException e1) { 22                     e1.printStackTrace(); 23                 } 24             } 25             e.printStackTrace(); 26         }finally{ 27             JdbcUtil.release(rs, stmt, conn); 28         } 29          30     } 31 }

事务的特性(ACID)

原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
 
一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

事务的隔离级别

多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性.

1、多线程机制下,不考虑把不同线程中的事务隔离起来,会导致什么问题?
脏读:指一个线程中的事务读到了另外一个线程中事务未提交的数据。
不可重复读:指一个线程中的事务读到了另外一个线程中提交的UPDATE的数据。
虚读(幻读):指一个线程中的事务读到了另外一个线程中提交的INSERT的数据。

2、解决以上问题:
更改事务的隔离级别:
1 READ UNCOMMITTED:脏读、不可重复读、虚读都有可能发生。
2 READ COMMITTED:防止脏读,不可重复读、虚读都有可能发生。
4 REPEATABLE READ:防止脏读、不可重复读,虚读有可能发生。
8 SERIALIZABLE:防止脏读、不可重复读、虚读的发生。
随着级别越高,数据越安全,但效率越低。

3、MySQL查看和更改事务的隔离级别
查看当前的隔离级别:SELECT @@TX_ISOLATION;
临时更改当前的隔离级别(一定要在开启事务前):
  SET TRANSACTION ISOLATION LEVEL 四个级别之一

  Connection中的setTransactionIsolation(int level)用于设置隔离级别
  Level:Connection中的常量

先设置隔离级别,在开启事务。
  Conn.setTransactionIsolation(Connection.*);
  Conn.setAutoCommit(false);

阅读全文
0 0
原创粉丝点击