当前位置: 首页 > news >正文

2014-07-14 Java Web的学习(11)-----数据库分页事务简单理解

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

1、什么是分页?          

分页,是一种将所有数据分段展示给用户的技术.用户每次看到的不是全部数据,其中的一部分,如果在其中没有找到自习自己想要的内容,用户可以通过制定页码或是翻页的方式转换可见内容,直到找到自己想要的内容为止.其实这和我们阅读书籍很类似.

下面显示了网络常用的几种的分页方式:

QQ拼音截图未命名                      
QQ拼音截图未命名 [1]                      


2、分页的意义

分页确实有效,但它一定会加大系统的复杂度,但可否不分页呢?如果数据量少的话当然可以.但是对于企业信息系统来说数据量不会限制在一个小范围内.如果不顾一切的Select * from某个表,再将返回的数据一古脑的扔给客户,即使客户能够忍受成千上万足够让人眼花缭乱的表格式数据,繁忙的网络,紧张的服务器也会提出它们无声的抗议,甚至有时会以彻底的罢工作为终结.这个结局有点像古代为所欲为的暴君和他忍无可忍的臣民之间的故事.

 程序员不是暴君,他希望程序使生活变得更好而不是更糟.考虑到企业信息系统多是三层甚至更多层架构的事实,程序员在向客户展示数据时都应该采取分页的形式.如果他不想被抱怨淹没或是半夜被电话惊醒的话.                

3、在什么地方进行分页处理(数据库进行分页处理)

在进行分页之前,让我们回想一下.在典型的三层架构中,从请求到返回数据到客户端的过程.如下图所示:

0_1276095177DC94.gif

从上面的图中我们可以观察得知,在SQL语句处理完毕后,数据库,WebApplication和Browser都能进行分页,那在哪里分页好呢?                              
判断的标准是速度,显而易见,数据库服务器,Web应用服务器和客户端之间是网络,如果网络传递的数据量越少,则客户端获得响应的速度越快.而且一般来说,数据库服务器和Web应用服务器的处理能力一般比客户端要强很多.从这两点来看,在客户端分页的方案是最不可取的.                            
其次就剩下了在Web服务器端分页和在数据库端分页两种方式了,如果选择在Web服务器端分页的话,大部分的被过滤掉的数据还是被传输到了Web应用服务器端,与其这样还不如直接在数据库端进行分页.

因此比较好的分页做法应该是每次翻页的时候只从数据库里检索页面大小的块区的数据。这样虽然每次翻页都需要查询数据库,但查询出的记录数很少,网络传输数据量不大,如果使用连接池更可以略过最耗时的建立数据库连接过程。而在数据库端有各种成熟的优化技术用于提高查询速度,比在应用服务器层做缓存有效多了。

4、分页的SQL语句

如果我们使用JDBC访问数据库服务器的数据的话,那么不同的类型数据库的的采用的分页SQL语句(方言)是不一样滴.
1.MySQL DataBase                    
 查询MySQL5的文档中找到这一段内容:LIMIT子句可以被用于限制被SELECT语句返回的行数.LIMIT取一个或两个数字自变量,自变量必须是非负的整数常数(当使用已预备的语句时除外),使用两个自变量时,第一个自变量指定返回的第一行的偏移量,第二个自变量指定返回的行数的最大值。初始行的偏移量为0(不是1):                    

mysql> SELECT * FROM tbl LIMIT 5,10;  # Retrieve rows 6-15  偏移量为5,而第一行的偏移量为0,所以从第六行记录开始,取10条.
对于这一SQL语句自己的理解是:Limit后的两个参数中,参数1是起始下标,它从0开始,开始行记录就是(参数1)+1;参数2是返回的记录数。

2.Oraclie DataBase

在Oracle数据库中,分页方式没有MySql这样简单,它需要依靠rownum来实现.Rownum表示一条记录的行号,值得注意的是它在获取每一行后才赋予.因此,想指定rownum的区间来取得分页数据在一层查询语句中是无法做到的,要分页还要进行一次查询.下图是分页的具体示例,它从帐户表中返回前十条记录.

fenye04

这是由于CBO优化模式下,Oracle可以将外层的查询条件推到内层查询中,以提高内层查询的执行效率。对于第一个查询语句,第二层的查询条件WHERE ROWNUM <= 40就可以被Oracle推入到内层查询中,这样Oracle查询的结果一旦超过了ROWNUM限制条件,就终止查询将结果返回了。

5、MySQL+Java代码实现分页                

像如平时我们很在分页中看到的,分页的时候返回的不仅包括查询的结果集(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";   }
  

6、无刷新的AJAX分页技术

代码中每次通过servlet请求取得结果集后,都会转向到一个jsp页面显示结果,这样每次查询页面都会刷新一下,比如查询出现结果集后要查看第三页,页面就会刷新一下。这样页面给人的效果感觉就会有点不舒服,所以我们希望能够在通过条件查询结果集后无论访问哪一页,页面都不会刷新,而只是结果集变化。

要解决这个,我想大家很容易就会想到Ajax.

是啊,这就要请Ajax出山了。当通过查询条件查询到结果集后,以后每次访问任何一页都通过Ajax来访问,使用异步Ajax与Servlet进行交互,将结果查询出来返回给Ajax,这样页面内容因为Ajax返回结果而改变,而页面却不会刷新,这就实现了无刷新的分页技术.

以后使用JQuery+JQuery.Pagination框架实现.

 参考博客及实现:

http://blog.csdn.net/xiaoyousifang/article/details/5824802

7、MySQL中处理大文本

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

8、MySQL中批处理

业务场景:当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。
实现批处理有两种方式:
  • 第一种方式:Statement.addBatch(sql) list

  •  第二种方式:PreparedStatement.addBatch() 注意内存会溢出.

QQ拼音截图未命名 [2]                  

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


9、MySQL存储过程(翻阅MySQL文档)


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


10、MySQL数据库的事务(重点)

 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.事务的四大特性
  • 原子性:原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生.

  • 一致性:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。比如转账:转账前aaa+bbb=2000;转账后aaa+bbb=2000.

  • 隔离性:事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离.

  • 持久性:指一个事务一旦被提交,它对数据库中数据的改变就是永久性的.

11、事务隔离级别

 

1.如果不考虑事务的隔离性,会出现什么问题?

脏读:一个线程中的事务读到了另一个线程中事务未提交的数据.

不可重复读:一个线程中的事务读到了另一个线程中提交的update的数据,前后两次读到的内容不一致.

虚读:一个线程中的事务读到了另一个线程中提交的insertdelete的数据,前后读到的记录条数不一致.

 PS:主要不同事务之间不能互相影响.&#160;


2.事务的隔离级别
  • READ UNCOMMITTED 脏读、不可重复读、虚读都有可能发生

  • READ COMMITTED 能避免脏读;不可重复读、虚读有可能发生(Oracle默认)

  • REPEATABLE READ 能避免脏读、不可重复读;虚读有可能发生(MySQL默认)

  • SERIALIZABLE 能避免脏读、不可重复读、虚读的发生

  • 以上隔离级别:从上到下,级别越高,性能越低,数据也越安全.



3.手动演示脏读、不可重复读、虚读
  • 查看当前数据库的隔离级别:select @@tx_isolation;

  • 更改隔离级别开启事务之前更改set transaction isolation level 四个级别之一.

  • 演示account表信息如下:

 QQ拼音截图未命名 [3]                      

演示脏读:

时间

线程1(自己) READ UNCOMMITTED

线程2

说明

t1

start transaction;



t2

select * from account where name='aaa';发现:1000元



t3
start transaction
t4
update account set money=money+100 where name='aaa'
t5select * from account where name='aaa';发现:1100元
脏读发生了
t6commit;

t7
commit;


演示不可重复读:

时间线程1(自己) READ COMMITTED线程2说明

t1

start transaction;



t2

select * from account where name='aaa';发现:1000



t3

start transaction;


t4

update account set money=money+100 where name='aaa';


t5

select * from account where name='aaa';发现:1000


避免了脏读
t6
commit
t7

select * from account where name='aaa';发现:1100


发生了不可重复读
t8commit;

 演示幻读或者虚读:


线程1(自己REPEATABLE READ

线程2

说明

t1

start transaction;



t2

select * from account;发现:3条记录



t3

start transaction


t4
insert into account values(4,'ddd',1000);
t5
commit;
t6select * form account;发现4条记录
幻影数据.发生了欢读。
t7commit;


 4.在JDBC中设置隔离级别              
 

Connection.setTransactionIsolation(int level);

图片2


 5.丢失更新
 问题:什么是丢失更新?
答:a.一个线程中事务覆盖另一个线程中事务已经提交的数据.
b.撤销一个事务,影响已提交事务.事实上就是上面的三种情况.                  
图片3                  
这里主要说明a项中丢失更新的处理.我们可以设置隔离级别来控制丢失更新,但是为了避免错误的读写,还需要学习乐观锁和悲观锁.                  
悲观锁:其他事务会同时访问资源,就是上锁。它是数据库的锁机制,不是程序模块实现的,因为可能多个系统在动数据库嘛

?

1

2

 select * from person for update ;

update  person set name='ff' where id='1'

  非得提交了之后锁才能释放。所以第一句只要不执行完,其他的事务休想动符合条件的记录(就是该表的数据).

   在hibernate里实现悲观锁:

?

1

2

3

4

String hqlStr ="from TUser as user where user.name='itheima'";

Query query = session.createQuery(hqlStr);

query.setLockMode("user",LockMode.UPGRADE); // 加锁

List userList = query.list();// 执行查询,获取数据

 类名是TUser 字段是user,原理上还是数据库的for update子句

  乐观锁:访问时其他事务修改的概率很低,大不了重读一次。完全靠隔离级别,如果一次操作开销很大,建议用悲观锁。数据库里用版本号实现,说白了就是有个version字段

  比如:

?

1

2

3

select *  from person ;

select *  from person ;

update person set name='xiaoming',version=version+1 where  id='1' and version=0;

 找不到版本号就重新来呗!这就避免了第二类丢失和不可重复读.(SVN版本控制器原理)

 举个例子,比如AB都在修改账户里的钱,版本号0,当前100¥,A读了100,扣了20,交上去就是80,版本号1,B如果没同时读,版本号改完就是2,也就没问题了。如果真和A同时读的,也读了个100¥,那它修改后的版 本号就是1,不大于当前版本号,所以不能更新(数据操作删除或更新),只好重新操作一遍咯!这个不难理解吧?

 当然乐观锁也会出点小麻烦,因为它是程序实现的,不是数据库实现的,所以脏读是没法避免的。比如这个系统回滚了,别的系统可不知道!

 在hibernate里实现

?

1

2

3

4

5

6

7

8

9

10

11

<hibernate-mapping>

<class

name="org.hibernate.sample.TUser"

table="t_user"

dynamic-update="true"

dynamic-insert="true"

optimistic-lock="version"

>

……

</class>

</hibernate-mapping>

  例子选自http://www.blogjava.net/loocky/archive/2006/11/15/81138.html

 这样对象TUser写入关系型数据库用的就是乐观锁了,optimistic-lock="version"表示用版本号控制.

 end......................

转载于:https://my.oschina.net/codeWatching/blog/398450

相关文章:

  • 学习札记
  • 文件系统管理
  • poj2538
  • Web开发的那点事--业务层常用功能
  • WPF注意点
  • AS400 Sequel View报表学习笔记 (一)
  • 2013年工作中遇到的20个问题:141-160
  • What means the error-message 'java.lang.OutOfMemoryError: GC overhead limit exceeded' in Java?
  • 大家好
  • getBoundingClientRect() 来获取页面元素的位置
  • 索引(二)
  • 《优秀网页设计速查与赏析》464
  • 分享Silverlight/WPF/Windows Phone一周学习导读(05月16日-05月21日)
  • 2015 UESTC 数据结构专题D题 秋实大哥与战争 SET的妙用
  • Sass和Compass入门
  • hexo+github搭建个人博客
  • 【159天】尚学堂高琪Java300集视频精华笔记(128)
  • 03Go 类型总结
  • cookie和session
  • ES6--对象的扩展
  • HTTP中的ETag在移动客户端的应用
  • mysql 数据库四种事务隔离级别
  • PHP的Ev教程三(Periodic watcher)
  • spring cloud gateway 源码解析(4)跨域问题处理
  • unity如何实现一个固定宽度的orthagraphic相机
  • vue--为什么data属性必须是一个函数
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 包装类对象
  • 大型网站性能监测、分析与优化常见问题QA
  • 基于OpenResty的Lua Web框架lor0.0.2预览版发布
  • 检测对象或数组
  • 模型微调
  • 一道闭包题引发的思考
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 再谈express与koa的对比
  • ionic异常记录
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • 阿里云移动端播放器高级功能介绍
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • #HarmonyOS:基础语法
  • #pragma once
  • #在 README.md 中生成项目目录结构
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (30)数组元素和与数字和的绝对差
  • (C语言)fgets与fputs函数详解
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  • (十七)devops持续集成开发——使用jenkins流水线pipeline方式发布一个微服务项目
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .NetCore项目nginx发布
  • .Net程序猿乐Android发展---(10)框架布局FrameLayout
  • ?php echo $logosrc[0];?,如何在一行中显示logo和标题?
  • @property @synthesize @dynamic 及相关属性作用探究
  • @软考考生,这份软考高分攻略你须知道