Java框架之Hibernate(四)
本文主要介绍:
1 悲观锁和乐观锁
2 使用版本号控制并发访问
3 flush方法和批量更新的问题
4 DetachedCriteria
5 N + 1 次查询
6 使用sql进行查询
7 注解方式
一、悲观锁和乐观锁
问题:有两个人,同时打开了一个贴子,进行编辑,结果会怎么样? 后提交的,有可能会把先提交的冲掉。
比如一个贴子,发表者是张三
第一个人,改成李四,第二个改成王五,后提交,结果最后是王五,李四没了
==悲观锁: 操作员得到数据以后,别人就不能修改,直到他修改完成,事务提交后才能进行
==乐观锁: 加标志位,以标位(版本号)的形式进行控制
假如说读过来的时候它的版本号是0,然后它就会去提交,提交会更新这个版本号,比如给它加一,如果同时有另一个人也读了这个版本是0的数据,他后提交,提交的时候,发现版本号不是0了,证明这东西被别人动过了,要么重新读入,要么放弃修改。
Hibernate支持乐观锁。当多个事务同时对数据库表中的同一条数据操作时,如果没有加锁机制的话,就会产生脏数据(duty data)。Hibernate有2种机制可以解决这个问题:
乐观锁和悲观锁。这里我们只讨论乐观锁。
Hibernate乐观锁,能自动检测多个事务对同一条数据进行的操作,并根据先胜原则,提交第一个事务,其他的事务提交时则抛出org.hibernate.StaleObjectStateException异常。
二、使用版本号控制并发访问
1) 在数据库中增加一个字段, versionNo (叫什么名都可以),整型 (或timestamp 型)
2) 对应的实体类中,也加一个相同的字段
3) 在配置文件 Userinfo.xml 中增加一个配置 <version name="versionNo" /> //放在id之后,所有的其他属性之前
//例子 Userinfo user1=(Userinfo) HibUtil.get(Userinfo.class,505 ); Userinfo user2=(Userinfo) HibUtil.get(Userinfo.class,505 ); //user1 和 user2 对应着数据库中的同一条数据 try{ user1.setUserName("这是张三给起的名"); HibUtil.update(user1); user2.setUserName("这是赵明明给启的名"); HibUtil.update(user2); } catch(StaleObjectStateException ex ){ System.out.println("数据已被别人修改!保存失败"); } /*org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction */
三、flush方法和批量更新的问题
//例一 public static void main(String[] args) { Session s=HibUtil.getSession(); Userinfo user=new Userinfo(); user.setUserName("aaa"); Userinfo user2=new Userinfo(); user2.setUserName("aaa"); Transaction tx=s.beginTransaction(); s.save(user); //key s.save(user2); // //s.flush(); //强制进行缓存的同步 System.out.println("--------我是不是先打印???----------"); //横线出现在save之前 tx.commit(); HibUtil.closeSession(); }
说明:
1)如果主键生成器是hilo
在不写 flush的情况下,可以发现横线是先输出,再执行save,因为hibernate总是试图把进行数据库访问的操作往后拖。上面的两条save语句,实际上是先把数据添到一级缓存中了,在最后commit 的时候,再进行数据库访问
2)如果加上 s.flush(); 表示强制要求hibernate刷新一级缓存中的数据,这时hibernate被迫,要将缓存中的数据存到数据库中,所以必须执行sql语句, 横线就会出现在save 之后了。
3)如果 主键生成器改成 native , 由于hibernate只有在知道主键的情况下才能进行一级缓存的填充,而native主键生成器是必须把数据保存到数据库以后才能得到生成的主键的,所以save方法会先执行。
用StatelessSession接口:它不和一级缓存、二级缓存交互,也不触发任何事件、监听器、拦截器,通过该接口的操作会立刻发送给数据库,与JDBC的功能一样。
StatelessSession s = sessionFactory.openStatelessSession(); 该接口的方法与Session类似
StatelessSession 没有 save 但有 insert
四、DetachedCriteria
最大的好处,是可以在Session外进行创建
//例子 public class UserAction { public static void main(String[] args) { Scanner scan=new Scanner(System.in); System.out.println("请输入用户名"); String userName=scan.nextLine(); System.out.println("请输备注"); String note=scan.nextLine(); DetachedCriteria dc= DetachedCriteria.forClass(Userinfo.class); if(userName!=null && (!"".equals(userName))){ dc.add(Restrictions.eq("userName", userName)); } if(note!=null && (!"".equals(note))){ dc.add(Restrictions.eq("note", note)); } UserDao dao=new UserDao(); List<Userinfo> userList=dao.search(dc); System.out.println("================结果================"); for (Userinfo u : userList) { System.out.println(u); } } } public class UserDao { // 多条件查询 public List<Userinfo> search(DetachedCriteria dc) { try { Session s = HibUtil.getSession(); Criteria c = dc.getExecutableCriteria(s); return c.list(); } finally { HibUtil.closeSession(); } } }
五、N + 1 次查询
用Query.iterator可能会有 N+1次查询。n+1次查询,是指第一次,只查出来id, 然后才查它的内容,而且多数是从缓存里拿的。
// 注意,这里在使用 q.iterate() 这种迭代的时候才会出现 N+1 次查询 public static void queryUser(){ Session s=HibUtil.getSession(); Query q=s.createQuery("from UserInfo"); Iterator<UserInfo> it=q.iterate(); while(it.hasNext()){ UserInfo u=it.next(); System.out.println(u.getUserName()); } s.close(); }
六、使用sql进行查询
例一:
public static void main(String[] args) { List<Userinfo > userList=getUserList(); for (Userinfo u : userList) { System.out.println(u); } } public static List<Userinfo> getUserList() { try { Session s = HibUtil.getSession(); // s.createQuery(queryString); String sql = "select * from userinfo"; //用的sql语句,后面是表名 SQLQuery q = s.createSQLQuery(sql).addEntity(Userinfo.class); //不要忘了指定类型 return q.list(); } finally { HibUtil.closeSession(); } }
例二 :
使用sql进行关联查询,返回的结果 List<Object[]>
//main 方法 : List<Object[]> listObj=getUserList2(); for (Object[] fields : listObj) { for (Object field : fields) { System.out.print(field +"\t"); } System.out.println(); } public static List<Object[]> getUserList2(){ try { Session s = HibUtil.getSession(); String sql="select a.id,a.fahao,b.name from nigu a left join niguan b on a.niguan_id=b.id"; SQLQuery q = s.createSQLQuery(sql); return q.list(); } finally { HibUtil.closeSession(); } }
例三:
使用Dto进行数据传输 (Dto 是数据传输对象)
//main 方法: List<NiGuDto> niguList=getUserList3(); for (NiGuDto n : niguList) { System.out.println(n); } public static List<NiGuDto> getUserList3(){ try { Session s = HibUtil.getSession(); String sql="select a.id,a.fahao,b.name from nigu a left join niguan b on a.niguan_id=b.id"; Query q=s.createSQLQuery(sql); q.setResultTransformer(Transformers.aliasToBean(NiGuDto.class)); //进行数据传输对象的处理 return q.list(); } finally { HibUtil.closeSession(); } }
七、注解方式
1) 导包
2) 配置
@Entity @Table(name="User_info") public class Userinfo implements java.io.Serializable { private String note; private Integer versionNo; private Integer id; //主键相关的注解一般都放在get方法 private String userName; @Id @GeneratedValue public Integer getId() { return this.id; } .... }
注意:
//1.在主配置文件中要加入 <mapping class="cat.beans.Userinfo" /> //2.在工具类中的 //Configuration cxf=new Configuration(); 要换成下面的写法 Configuration cxf=new AnnotationConfiguration(); //3.3 要这样做
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程