分页,是一种将所有数据分段展示给用户的技术.用户每次看到的不是全部数据,其中的一部分,如果在其中没有找到自习自己想要的内容,用户可以通过制定页码或是翻页的方式转换可见内容,直到找到自己想要的内容为止.其实这和我们阅读书籍很类似.
下面显示了网络常用的几种的分页方式:
|
分页确实有效,但它一定会加大系统的复杂度,但可否不分页呢?如果数据量少的话当然可以.但是对于企业信息系统来说数据量不会限制在一个小范围内.如果不顾一切的Select * from某个表,再将返回的数据一古脑的扔给客户,即使客户能够忍受成千上万足够让人眼花缭乱的表格式数据,繁忙的网络,紧张的服务器也会提出它们无声的抗议,甚至有时会以彻底的罢工作为终结.这个结局有点像古代为所欲为的暴君和他忍无可忍的臣民之间的故事.
程序员不是暴君,他希望程序使生活变得更好而不是更糟.考虑到企业信息系统多是三层甚至更多层架构的事实,程序员在向客户展示数据时都应该采取分页的形式.如果他不想被抱怨淹没或是半夜被电话惊醒的话.
在进行分页之前,让我们回想一下.在典型的三层架构中,从请求到返回数据到客户端的过程.如下图所示:
从上面的图中我们可以观察得知,在SQL语句处理完毕后,数据库,WebApplication和Browser都能进行分页,那在哪里分页好呢? 因此比较好的分页做法应该是每次翻页的时候只从数据库里检索页面大小的块区的数据。这样虽然每次翻页都需要查询数据库,但查询出的记录数很少,网络传输数据量不大,如果使用连接池更可以略过最耗时的建立数据库连接过程。而在数据库端有各种成熟的优化技术用于提高查询速度,比在应用服务器层做缓存有效多了。 |
如果我们使用JDBC访问数据库服务器的数据的话,那么不同的类型数据库的的采用的分页SQL语句(方言)是不一样滴. 1.MySQL DataBase 查询MySQL5的文档中找到这一段内容:LIMIT子句可以被用于限制被SELECT语句返回的行数.LIMIT取一个或两个数字自变量,自变量必须是非负的整数常数(当使用已预备的语句时除外),使用两个自变量时,第一个自变量指定返回的第一行的偏移量,第二个自变量指定返回的行数的最大值。初始行的偏移量为0(不是1):
2.Oraclie DataBase 在Oracle数据库中,分页方式没有MySql这样简单,它需要依靠rownum来实现.Rownum表示一条记录的行号,值得注意的是它在获取每一行后才赋予.因此,想指定rownum的区间来取得分页数据在一层查询语句中是无法做到的,要分页还要进行一次查询.下图是分页的具体示例,它从帐户表中返回前十条记录. 这是由于CBO优化模式下,Oracle可以将外层的查询条件推到内层查询中,以提高内层查询的执行效率。对于第一个查询语句,第二层的查询条件WHERE ROWNUM <= 40就可以被Oracle推入到内层查询中,这样Oracle查询的结果一旦超过了ROWNUM限制条件,就终止查询将结果返回了。 |
像如平时我们很在分页中看到的,分页的时候返回的不仅包括查询的结果集(List),而且还包括总的页数(totalPageNum)、当前第几页(currentPageNum)等等信息,所以我们封装一个查询结果Page类,代码如下:其中使用泛型是为了能使的该分页类能进行重用,比如在查询用户时可以封装User对象、在查询财务中的流向单时可以封装流向单FlowCard类. |
package com.itheima.web.formbean;import java.io.Serializable;import java.util.List;/** * 分页模板类 * @author LISAI */public class Page<T> implements Serializable { private static final long serialVersionUID = 1L; /*每个页面数据集合*/ private List<T> totalRecords; /*每个页面最多记录条数*/ private int pageMaxSize =10; //开发时,通过web.xml文件参数配置. /*当前页码*/ private int currentPageNum ; /*总共多少页,总页数*/ private int totalPageNum ; /*总记录数*/ private int totalRecordsNum ; /*每页的开始索引数*/ private int startIndex ; public Page( int currentPageNum,int totalRecordsNum){ //获取当前页码 this.currentPageNum = currentPageNum; //获取记录 this.totalRecordsNum = totalRecordsNum; //计算总页数 this.totalPageNum = totalRecordsNum%pageMaxSize ==0?totalRecordsNum/pageMaxSize:totalRecordsNum/pageMaxSize+1; if(this.currentPageNum >this.totalPageNum){ this.currentPageNum = this.totalPageNum; } //计算每页的开始索引数 this.setStartIndex((this.currentPageNum-1)*pageMaxSize); //判断总页数<=9 if(this.totalPageNum <=9){ this.startPageNum = 1; this.endPageNum = this.totalPageNum; } else{ this.startPageNum = this.currentPageNum-4; this.endPageNum = this.currentPageNum+4; if(this .startPageNum <1){ this.startPageNum = 1; this.endPageNum = this.startPageNum+8; } if(this .endPageNum >this.totalPageNum){ this.endPageNum = this.totalPageNum; this.startPageNum = this.totalPageNum -8; } } } /*上一页*/ @SuppressWarnings( "unused") private int proviousPageNum ; /*下一页*/ @SuppressWarnings( "unused") private int nextPageNum ; /*开始页码*/ private int startPageNum ; /*结束页码*/ private int endPageNum ; public int getProviousPageNum() { //如果当前页码-1大于0时,表示存在前一页.如果小于0,则直接赋值为首页. return proviousPageNum=(this .currentPageNum -1>0?this.currentPageNum-1:1); } public void setProviousPageNum(int proviousPageNum) { this.proviousPageNum = proviousPageNum; } public int getNextPageNum() { //如果当前页码+1大于总页数,则直接为尾页.如果不是,表示存在后一页. return nextPageNum=(this .currentPageNum +1>totalPageNum ?totalPageNum :this.currentPageNum+1); } public void setNextPageNum(int nextPageNum) { this.nextPageNum = nextPageNum; } public List<T> getTotalRecords() { return totalRecords ; } public void setTotalRecords(List<T> totalRecords) { this.totalRecords = totalRecords; } public int getPageMaxSize() { return pageMaxSize ; } public void setPageMaxSize(int pageMaxSize) { this.pageMaxSize = pageMaxSize; } public int getCurrentPageNum() { return currentPageNum ; } public void setCurrentPageNum(int currentPageNum) { this.currentPageNum = currentPageNum; } public int getTotalPageNum() { return totalPageNum ; } public void setTotalPageNum(int totalPageNum) { this.totalPageNum = totalPageNum; } public long getTotalRecordsNum() { return totalRecordsNum ; } public int getStartIndex() { return startIndex ; } public void setStartIndex(int startIndex) { this.startIndex = startIndex; } public void setTotalRecordsNum(int totalRecordsNum) { this.totalRecordsNum = totalRecordsNum; } public int getStartPageNum() { return startPageNum ; } public void setStartPageNum(int startPageNum) { this.startPageNum = startPageNum; } public int getEndPageNum() { return endPageNum ; } public void setEndPageNum(int endPageNum) { this.endPageNum = endPageNum; }} |
以查询用户为例,用户选择查询条件.首先调用Servlet获取查询参数,然后请求业务逻辑层取得分页封装结果类。业务逻辑调用Dao层取得结果集、取得中记录数封装成分页类。最后Servlet将结果设置到JSP页面显示. |
首先来讲解Servlet,代码如下: |
在service(request,response)方法中调用下面这个方法. // 查询分页数据 private void findAllCustomer(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { String currentPageNum = request.getParameter("currentPageNum" ); Page<Customer> page=customerService.findPageRecords(currentPageNum); request.setAttribute( "page", page); request.getRequestDispatcher( "/jsps/listCustomers.jsp").forward(request, response); } catch (Exception e) { e.printStackTrace(); } } |
接着是业务逻辑层Service代码,代码如下: |
/** * 查询所有客户信息,并且分页显示 * @param currentPageNum * @return Page 封装分页信息的对象 */ public Page<Customer> findPageRecords(String currentPageNum) { int totalRecordsNum = customerDao.getTotalRecords(); int pageNum = 1; if(currentPageNum!=null&&!currentPageNum.trim().equals( "")){ try { pageNum = Integer.parseInt(currentPageNum); } catch (NumberFormatException e) { throw new RuntimeException("SB,页码必须是整数"); } } Page<Customer> page = new Page<Customer>(pageNum, totalRecordsNum); List<Customer> totalRecords = customerDao.findPageRecords(page.getStartIndex(), page.getPageMaxSize()); page.setTotalRecords(totalRecords); return page; } |
最后是Dao层代码实现,代码如下: |
//关于数据库的操作无非是SQL语句和参数的不同,所以下面就直接写SQL语句代替实现. /** * 查询分页数据 * @param startIndex 开始记录的索引 * @param pageSize 一次查询的条数 * @return List<Customer> */ public List<Customer> findPageRecords( long startIndex, int pageSize){ String sql = "select * from tb_customer limit ?,?"; } /** * 获取数据库有关客户信息总记录数 * @return int */ public int getTotalRecords(){ String sql = "select count(*) from tb_customer"; } |
代码中每次通过servlet请求取得结果集后,都会转向到一个jsp页面显示结果,这样每次查询页面都会刷新一下,比如查询出现结果集后要查看第三页,页面就会刷新一下。这样页面给人的效果感觉就会有点不舒服,所以我们希望能够在通过条件查询结果集后无论访问哪一页,页面都不会刷新,而只是结果集变化。
要解决这个,我想大家很容易就会想到Ajax.
是啊,这就要请Ajax出山了。当通过查询条件查询到结果集后,以后每次访问任何一页都通过Ajax来访问,使用异步Ajax与Servlet进行交互,将结果查询出来返回给Ajax,这样页面内容因为Ajax返回结果而改变,而页面却不会刷新,这就实现了无刷新的分页技术.
以后使用JQuery+JQuery.Pagination框架实现.
参考博客及实现:
http://blog.csdn.net/xiaoyousifang/article/details/5824802
Text类型和Blob类型(二进制) package com.ithiema.jdbc.lob;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.FileReader;import java.io.FileWriter;import java.io.InputStream;import java.io.OutputStream;import java.io.Reader;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import org.junit.Test;import com.itheima.utils.JdbcUtil;public class LobDemo1 { @Testpublic void testWrtieLob(){ Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null;try { conn = JdbcUtil.getConnection(); stmt = conn.prepareStatement("insert into t1(id,content) values(?,?)"); stmt.setInt(1, 1); File file = new File("src/a.txt"); FileReader reader = new FileReader(file); stmt.setCharacterStream(2, reader, (int)file.length()); stmt.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } finally{ JdbcUtil.release(conn, stmt, rs); } } @Testpublic void testReadLob(){ Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null;try { conn = JdbcUtil.getConnection(); stmt = conn.prepareStatement("select id,content from t1"); rs=stmt.executeQuery();while(rs.next()){ System.out.println(rs.getInt("id")); FileWriter writer = new FileWriter("d:/a.txt"); Reader reader=rs.getCharacterStream("content");int len = -1;char[] ch = new char[1024];// String str=null;while((len=reader.read(ch))!=-1){// str+=new String(ch); writer.write(ch, 0, len); } reader.close();// System.out.println(str); writer.close(); } } catch (Exception e) { e.printStackTrace(); } finally{ JdbcUtil.release(conn, stmt, rs); } } @Testpublic void testWrtieBlob(){ Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null;try { conn = JdbcUtil.getConnection(); stmt = conn.prepareStatement("select id,content from t2"); rs=stmt.executeQuery();while(rs.next()){ OutputStream out = new FileOutputStream("d:/1.bmp"); InputStream in = rs.getBinaryStream("content");int len = -1;byte[] buff = new byte[1024];while((len=in.read(buff))!=-1){ out.write(buff); } in.close(); out.close(); } } catch (Exception e) { e.printStackTrace(); } finally{ JdbcUtil.release(conn, stmt, rs); } } @Testpublic void testReadBlob(){ Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null;try { conn = JdbcUtil.getConnection(); stmt = conn.prepareStatement("insert into t2(id,content) values(?,?)"); stmt.setInt(1, 1); InputStream in = new FileInputStream("src//1.bmp"); stmt.setBinaryStream(2,in,in.available()); stmt.executeUpdate(); in.close(); } catch (Exception e) { e.printStackTrace(); } finally{ JdbcUtil.release(conn, stmt, rs); } }} |
业务场景:当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。 实现批处理有两种方式:
package com.ithiema.jdbc.batch;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.Statement;import org.junit.Test;import com.itheima.utils.JdbcUtil;public class BatchDemo1 { @Testpublic void testBatch(){ Connection conn = null; Statement stmt = null; ResultSet rs = null;try { conn = JdbcUtil.getConnection(); stmt = conn.createStatement(); String sql1 = "insert into t3(id,name) values(1,'aaa')"; String sql2 = "insert into t3(id,name) values(2,'bbb')"; String sql3 = "insert into t3(id,name) values(3,'ccc')"; String sql4 = "delete from t3 where id=1"; stmt.addBatch(sql1); stmt.addBatch(sql2); stmt.addBatch(sql3); stmt.addBatch(sql4); stmt.executeBatch(); } catch (Exception e) { e.printStackTrace(); } finally{ JdbcUtil.release(conn, stmt, rs); } } @Testpublic void testBatch1(){ Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null;try { conn = JdbcUtil.getConnection(); String sql = "insert into t3(id,name) values(?,?)"; stmt = conn.prepareStatement(sql);for(int i=1;i<6;i++){ stmt.setInt(1, i); stmt.setString(2, "itheima"+i); stmt.addBatch(); } stmt.executeBatch(); } catch (Exception e) { e.printStackTrace(); } finally{ JdbcUtil.release(conn, stmt, rs); } } @Testpublic void testBatch2(){ Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null;try { conn = JdbcUtil.getConnection(); String sql = "insert into t3(id,name) values(?,?)"; stmt = conn.prepareStatement(sql);for(int i=0;i<10000;i++){ stmt.setInt(1, i+1); stmt.setString(2, "itheima"+i+1); stmt.addBatch();if(i%1000==0){ stmt.executeBatch(); stmt.clearBatch(); } } stmt.executeBatch(); } catch (Exception e) { e.printStackTrace(); } finally{ JdbcUtil.release(conn, stmt, rs); } }} |
package com.ithiema.jdbc.procedure;import java.io.File;import java.io.FileReader;import java.sql.CallableStatement;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.Types;import org.junit.Test;import com.itheima.utils.JdbcUtil;public class ProcedureDemo { @Testpublic void testProcedure(){ Connection conn = null; CallableStatement stmt = null; ResultSet rs = null;try { conn = JdbcUtil.getConnection(); stmt = conn.prepareCall("{call demoSp(?,?)}");//输入参数,设置值 stmt.setString(1, "itheima");//输出参数要注册类型 stmt.registerOutParameter(2, Types.VARCHAR);//执行存储过程 stmt.execute();//获取输出参数的值 String value = stmt.getString(2); System.out.println(value); } catch (Exception e) { e.printStackTrace(); } finally{ JdbcUtil.release(conn, stmt, rs); } }} |
1.事务的概念 事务指逻辑上的一组操作,例如一组SQL语句.要么一起全部执行成功,要么全部执行失败.本地事务和分布式事务的区别:List协议分析.JTA相关知识点. |
2.MySQL数据库对于事务的SQL语句 START TRANSACTION | BEGIN [WORK] COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
SET AUTOCOMMIT = {0 | 1}
START TRANSACTION或BEGIN语句可以开始一项新的事务 COMMIT可以提交当前事务,是变更成为永久变更 ROLLBACK可以 回滚当前事务,取消其变更 SET AUTOCOMMIT语句可以禁用或启用默认的autocommit模式,用于当前连接. 注意:MySQL数据库默认是自动提交模式即"一句SQL执行",就直接提交. |
3.在JDBC中使用事务(超级简单) 当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句. 若想关闭这种默认提交方式,让多条SQL在一个事务中执行.就执行以下语句: conn.setAutoCommit(false); //start transaction conn.rollback();//rollback transaction conn.commit();//commit transaction |
4.事务的四大特性
|
1.如果不考虑事务的隔离性,会出现什么问题? 脏读:一个线程中的事务读到了另一个线程中事务未提交的数据. 不可重复读:一个线程中的事务读到了另一个线程中提交的update的数据,前后两次读到的内容不一致. 虚读:一个线程中的事务读到了另一个线程中提交的insert或delete的数据,前后读到的记录条数不一致. PS:主要不同事务之间不能互相影响.  |
2.事务的隔离级别
|
3.手动演示脏读、不可重复读、虚读
演示脏读:
演示不可重复读:
演示幻读或者虚读:
|
4.在JDBC中设置隔离级别
|