• 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 +"	");
                            }
                            
                            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 要这样做
  • 相关阅读:
    Learn Orleans 04
    Learn Orleans 03
    Learn Orleans 02
    Learn Orleans 01
    Python 导入 Excel 到数据库
    visio 2016/2019 时序图/序列图,修改消息的实线/虚线 箭头问题 返回消息状态
    steeltoe简单使用
    AOP in .NET
    centos 8 docker安装以及兼容性问题处理
    ef core SoftDelete Multi-tenancy 软删除、多租户实现 Global Query Filters
  • 原文地址:https://www.cnblogs.com/1693977889zz/p/8213982.html
Copyright © 2020-2023  润新知