分页,批处理,大文本数据存储,事物处理
分页
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实现
@Override
public 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实现
@Override
public 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);
}
}
@Override
public 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}页
77 <a href="${pageContext.request.contextPath}">首页</a>
78 <a href="${pageContext.request.contextPath}/ControlServlet?op=showAllCustomers&num= ${page.prePageNum}">上一页</a>
79 <a href="${pageContext.request.contextPath}/ControlServlet?op=showAllCustomers&num=${page.nextPageNum}">下一页</a>
80 <a href="${pageContext.request.contextPath}/ControlServlet?op=showAllCustomers&num=${page.totalPageCount}">尾页</a>
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就代表一条语句。
@Test
public 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条记录
@Test
public 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);