一、大结果集分列
减少内存的占用率,分批次去数据库查询记录 (分页查询)
分页语句 不同数据库是不同的
MySQL: LIMIT M,N
M:是开始记录的索引。从0开始
N: 每次查询多少条
例如:
每次查10条:
查第一页的:SELECT * FROM customer LIMIT 0,10;
查第二页的:SELECT * FROM customer LIMIT 10,10;
实现分页步骤及代码
1.封装分页有关信息的类Page
public class Page {
private List records;// 显示的分页的记录信息
private int pageSize = 10;// 每页显示的记录条数
private int pageNum;// 当前页码
private int totalPageNum;// 总页数
private int startIndex;// 每页开始的记录索引
private int totalRecordsNum;// 总记录的条数
private int proviousPageNum;// 上一页页码
private int nextPageNum;// 下一页页码
private int startPage;// 页码:开始
private int endPage;// 页码:结束
// 查询记录的Servlet的url地址
private String url;
public Page(int pageNum, int totalRecordsNum) {
this.pageNum = pageNum;
this.totalRecordsNum = totalRecordsNum;
// 计算总页数
totalPageNum = totalRecordsNum % pageSize == 0 ? totalRecordsNum
/ pageSize : totalRecordsNum / pageSize + 1;
//计算每页开始记录的索引 当前页数-1*每页的记录条数
startIndex=(pageNum-1)*pageSize;
//页码计算
if(totalPageNum<=9){ //这里是实现页码条的计算 最多显示8条
startPage=1;
endPage=totalPageNum;
}else{
startPage=pageNum-4;
endPage=pageNum+4;
}
if(startPage<1){
startPage=1;
endPage=startPage+8;
}
if(endPage>totalPageNum){
endPage=totalPageNum;
startPage=totalPageNum-8;
}
}
public List getRecords() {
return records;
}
public void setRecords(List records) {
this.records = records;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getTotalPageNum() {
return totalPageNum;
}
public void setTotalPageNum(int totalPageNum) {
this.totalPageNum = totalPageNum;
}
public int getStartIndex() {
return startIndex;
}
public void setStartIndex(int startIndex) {
this.startIndex = startIndex;
}
public int getTotalRecordsNum() {
return totalRecordsNum;
}
public void setTotalRecordsNum(int totalRecordsNum) {
this.totalRecordsNum = totalRecordsNum;
}
public int getProviousPageNum() {
return pageNum-1>0?pageNum-1:1; //上一页页码等于当前页码-1如果是第一页不变
}
public void setProviousPageNum(int proviousPageNum) {
this.proviousPageNum = proviousPageNum;
}
public int getNextPageNum() {
//下一页页码等于当前页码+1如果已经超过总页数那就是不变
return pageNum+1>totalPageNum?totalPageNum:pageNum+1;
}
public void setNextPageNum(int nextPageNum) {
this.nextPageNum = nextPageNum;
}
public int getStartPage() {
return startPage;
}
public void setStartPage(int startPage) {
this.startPage = startPage;
}
public int getEndPage() {
return endPage;
}
public void setEndPage(int endPage) {
this.endPage = endPage;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
2.DAO的2个方法
// 得到记录的总条数
public int getTotalRecords() {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = JdbcUtil.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 DaoException(e);
} finally {
JdbcUtil.release(rs, stmt, conn);
}
}
// 根据开始索引和页面记录条数查询查询分页数据
public List<Customer> findPageRecords(int startIndex, int pageSize) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
stmt = conn
.prepareStatement("select id,name,gender,birthday,phone,email,hobby,type,description from customers limit ?,?");
stmt.setInt(1, startIndex);
stmt.setInt(2, pageSize);
rs = stmt.executeQuery();
// 查询到的数据保存到bean
List<Customer> cs = new ArrayList<Customer>();
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.setPhone(rs.getString("phone"));
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 DaoException(e);
} finally {
JdbcUtil.release(rs, stmt, conn);
}
}
3.业务处理方法
// 根据客户端查看页码查询数据
public Page findPageReocords(String num) {
int pageNum = 1;
if (num != null && !num.trim().equals("")) {
pageNum = Integer.parseInt(num);
}
int totalRecordsNum = dao.getTotalRecords();//获取总记录条数
Page page = new Page(pageNum, totalRecordsNum);
// 把记录搞进去
List<Customer> cs = dao.findPageRecords(page.getStartIndex(),
page.getPageSize());
page.setRecords(cs);
return page;
}
4.页面jsp实现
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!-- 分页开始 -->
第${page.pageNum}页/共${page.totalPageNum}页;
<a href="${pageContext.request.contextPath}${page.url}">首页</a>
<a
href="${pageContext.request.contextPath}${page.url}&num=${page.proviousPageNum}">上一页</a>
<!-- 这里实现上一页下一页效果 -->
<a
href="${pageContext.request.contextPath}${page.url}&num=${page.nextPageNum}">下一页</a>
<a
href="${pageContext.request.contextPath}${page.url}&num=${page.totalPageNum}">尾页</a>
<c:forEach begin="${page.startPage}" end="${page.endPage}" var="num">
<!-- 这里实现页码条 -->
<a href="${pageContext.request.contextPath}${page.url}&num=${num}">${num}</a>
</c:forEach>
<select id="s1">
<!-- 选项下拉页码-->
<c:forEach begin="1" end="${page.totalPageNum}" var="num">
<option value="${num}" ${page.pageNum==num? 'selected="selected"':''}>${num}</option>
</c:forEach>
</select>
<!--输入指定页码跳转-->
<input type="text" size="3" id="input1" value="" />
<input type="button" id="bt1" value="跳转" />
<script type="text/javascript">
window.οnlοad=function(){
document.getElementById("s1").οnchange=function(){
window.location.href="${pageContext.request.contextPath}${page.url}&num="+this.value;
}
document.getElementById("bt1").οnclick=function(){
var num = document.getElementById("input1");
//验证
if(!/^[1-9][0-9]*$/.test(num.value)){
alert("请输入正确的页码");
return;
}
if(num.value>${page.totalPageNum}){
alert("页码不能超过总页数");
return;
}
window.location.href="${pageContext.request.contextPath}${page.url}&num="+num.value;
}
}
</script>
<!-- 分页结束 -->
效果
二、JDBC处理大数据大文本和大二进制数据的存取
大数据也称之为LOB(Large Objects),LOB又分为:clob和blob
clob用于存储大文本。Text
blob用于存储二进制数据,例如图像、声音
对MySQL而言只有blob,而没有clob,mysql存储大文本采用的是Text,Text和blob分别又分为:
TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT
TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB
//大文本数据存储
@Test
public void testWriteClob(){
/*
* 需要的数据库
create table t1(
id int primary key,
content longtext
);
*/
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/dw.txt");
Reader reader=new FileReader(file);
stmt.setCharacterStream(2, reader,(int)file.length());
stmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtil.release(rs,stmt, conn);
}
}
@Test //大文本数据读取
public void testReadClob(){
Connection conn=null;
PreparedStatement stmt=null;
ResultSet rs=null;
try {
conn=JdbcUtil.getConnection();
stmt=conn.prepareStatement("select * from t1 where id=1");
rs=stmt.executeQuery();
if(rs.next()){
//存储到D盘
Writer w =new FileWriter("d:/dw.txt");
Reader r =rs.getCharacterStream("content");//获取指定列的值
char c[] =new char[1024];
int len =-1;
while((len=r.read(c))!=-1){
w.write(c,0,len);
}
r.close();
w.close();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtil.release(rs,stmt, conn);
}
}
@Test <span style="color:#3333ff;">//二进制的存储</span>
public void testWriteBlob(){
/*
create table t2(
id int primary key,
content longblob
);
*/
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/125q.jpg");
stmt.setBinaryStream(2, in,in.available());
stmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtil.release(rs,stmt, conn);
}
}
@Test //二进制的取出
public void testReadBlob(){
Connection conn=null;
PreparedStatement stmt=null;
ResultSet rs=null;
try {
conn=JdbcUtil.getConnection();
stmt=conn.prepareStatement("select * from t2 where id=1");
rs=stmt.executeQuery();
if(rs.next()){
//存储到当前目录
InputStream in =rs.getBinaryStream("content");//读取content值
OutputStream out =new FileOutputStream("d:/1.jpg");
int len =-1;
byte b[] =new byte[1024];
while((len=in.read(b))!=-1){
out.write(b,0,len);
}
in.close();
out.close();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtil.release(rs,stmt, conn);
}
}
三、批处理
JDBC提供了数据库batch处理的能力,在数据大批量操作(新增、删除等)的情况下可以大幅度提升系统的性能。
Statement:他的实现中。
有一个List集合,元素就是SQL语句
addBatch(sql);相当于 List.add(sql);
executeBatch();// 把List中的所有语句一次性的交给数据库处理
clearBatch();// 清空List中的元素
PreparedStatement:适合语句相同,但参数不同的批处理。
@Test //插入两条删除1条 批处理
public void testBatch1(){
/*create table t3(
id int primary key,
name varchar(100)
);*/
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try {
conn=JdbcUtil.getConnection();
stmt=conn.createStatement();
String sq1="insert into t3(id,name)values(1,'ww')";
String sq2="insert into t3(id,name)values(2,'gg')";
String sq3="delete from t3 where id =1";
stmt.addBatch(sq1);//把三条语句添加到List缓存中
stmt.addBatch(sq2);
stmt.addBatch(sq3);
stmt.executeBatch();//批量交给数据库处理
} catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtil.release(rs, stmt, conn);
}
}
@Test //向表中插入5条记录 语句一样 参数值不一样
public void testBatch2(){
Connection conn=null;
PreparedStatement stmt=null; //使用preparedStatement
ResultSet rs=null;
try {
conn=JdbcUtil.getConnection();
stmt=conn.prepareStatement("insert into t3(id,name) values(?,?)");
for(int i=0;i<10;i++){
stmt.setInt(1, i+1);
stmt.setString(2, "第"+i+"条");
stmt.addBatch();
}
stmt.executeBatch();
} catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtil.release(rs, stmt, conn);
}
}
@Test //使用批处理处理1W条数据
public void testBatch3(){
Connection conn=null;
PreparedStatement stmt=null;
ResultSet rs=null;
try {
conn=JdbcUtil.getConnection();
stmt=conn.prepareStatement("insert into t3(id,name) values(?,?)");
for(int i=0;i<10000;i++){
stmt.setInt(1, i+1);
stmt.setString(2, "第"+i+"条");
stmt.addBatch();
if(i%1000==0){ //防止内存溢出
stmt.executeBatch();
stmt.clearBatch();//清除缓存
}
}
stmt.executeBatch();
} catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtil.release(rs, stmt, conn);
}
}
四、存储过程 Stored Procedure
是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,相当于自定义的方法
<span style="color:#333333;">/*连接字符串 </span><span style="color:#ff0000;"> delimiter 是设置语句结束的符号 </span><span style="color:#333333;">
</span><span style="color:#ff0000;">delimiter</span><span style="color:#333333;"> $$
CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), INOUT inOutParam varchar(255))
BEGIN
SELECT CONCAT('连接字符串---', inputParam) into inOutParam;
END $$
delimiter ;
*/
//调用
public class ProcedureDemo {
@Test
public void test(){
Connection conn = null;
CallableStatement stmt = null;
ResultSet rs = null;
try{
conn = JdbcUtil.getConnection();
stmt = conn.prepareCall("{call demoSp(?,?)}");//固定的
//输入参数,设置值
stmt.setString(1, "值");
//输出参数要注册类型
stmt.registerOutParameter(2, Types.VARCHAR);
//执行存储过程
stmt.execute();
//获取输出参数的值
String value = stmt.getString(2);
System.out.println(value);
}catch(Exception e){
e.printStackTrace();
}finally{
JdbcUtil.release(rs, stmt, conn);
}
}</span>
五、事务
MySQL中事务是默认提交 一条语句就是一个事务
MySQL事务控制
start transaction:开启事务 后面的语言处于一个事务中
rollback:事务结束 不提交
commit:提交事务
JDBC控制事务方法
Start transaction =Connection.setAutoCommite(false)
rollback=Connection.rollback()
commit=Connection.commit()
六.事务的特性原理
1.原子性:原子性是指事务是一个不可分离;事务操作要么全部成功要么全部失败 例如转账给人 A成功转账给B B收到钱 是要么成功要么失败 不会发生A钱打出去了 B没有收到钱
2.一致性:事务必须使数据库从一个一致性状态到另一个一致性状态例如 A有100 B有100 合起来是200 A转账给B 50后 合计起来还是200 这就是一致性
3.隔离性:多个用户访问数据库时 数据库为每个用户开启一个线程不能被其他用户的事务操作数据干扰 多个用户间的并发事务要隔离 数据库在并发执行的事务间提供不同级别的隔离
4.持久性:是指事务成功结束后 它对数据库所做的跟新是永久保存的 即使发生系统崩溃 数据库重新启动 数据还可以恢复到事务成功结束时的状态
七、事务的隔离级别
如果不考虑事务隔离性可能会出现以下情况
1.脏读一个事务读到了另一个线程中未提交的数据
2.不可重复读:一个线程中的事务读到了另一个线程中Update的数据,前后读到的内容不一致
3.虚读 一个线程中的事务读到另一个线程中提交的insert或detele的数据,前后读到的记录条数不一致
事务的隔离级别:
READ UNCOMMITTED 脏读、不可重复读、虚读都有可能发生
READ COMMITTED 能避免脏读;不可重复读、虚读有可能发生(Oracle默认)
REPEATABLE READ 能避免脏读、不可重复读;虚读有可能发生(MySQL默认)
SERIALIZABLE 能避免脏读、不可重复读、虚读的发生
级别遇高 性能越低 数据越安全
MySQL:
查看当前数据库的隔离级别:
select @@tx_isolation;
更改隔离级别:(开启事务之前更改)
set transaction isolation level 四个级别之一
JDBC中设置隔离级别方法
Connection.setTransactionIsolation(int level);
八、丢失跟新
是指一个线程的事务覆盖了另一个线程已经提交的数据例如: 数据 id:1 name:A age=13
第一个线程查询数据 第二个线程查询数据
name:A 改为B name:A
age=13age=18 改为23
保存后结果应该是
id:1 name:B age:23 但是结果却是 id:1 name:A age=23 第二个线程覆盖了第一个
解决:利用数据库的锁机制
共享锁:可以有多把。
独占锁:(排它锁)只能有一把。只要外面有共享锁,会等。update、insert、delete会自动加独占锁。