zoukankan      html  css  js  c++  java
  • [Hibernate]知识点

    本笔记只介绍注解的方法

    一、准备工作:

    a、添加三个pojo类:

    Product:

    package pojo;
    
    import javax.persistence.*;
    import java.util.Set;
    
    @Entity
    @Table(name = "product_")
    public class Product {
        int id;
        String name;
        float price;
        Category category;
        Set<User> users;
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id")
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
    
        @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
        @JoinTable(
                name="user_product",
                joinColumns=@JoinColumn(name="pid"),
                inverseJoinColumns=@JoinColumn(name="uid")
        )
        public Set<User> getUsers() {
            return users;
        }
        public void setUsers(Set<User> users) {
            this.users = users;
        }
    
        @Column(name = "name")
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        @Column(name = "price")
        public float getPrice() {
            return price;
        }
        public void setPrice(float price) {
            this.price = price;
        }
        @ManyToOne
        @JoinColumn(name="cid")
        public Category getCategory() {
            return category;
        }
    
        public void setCategory(Category category) {
            this.category = category;
        }
    }
    View Code

    Category:

    package pojo;
    
    import javax.persistence.*;
    
    import java.util.Set;
    
    @Entity
    @Table(name = "category_")
    public class Category {
        int id;
        String name;
        Set<Product> products;
    
        @OneToMany(fetch=FetchType.EAGER)
        @JoinColumn(name="cid")
        public Set<Product> getProducts() {
            return products;
        }
    
        public void setProducts(Set<Product> products) {
            this.products = products;
        }
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id")
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    
    }
    View Code

    User:

    package pojo;
    
    import java.util.Set;
    
    import javax.persistence.CascadeType;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.JoinTable;
    import javax.persistence.ManyToMany;
    import javax.persistence.Table;
    
    @Entity
    @Table(name="user_")
    public class User {
    
        int id;
        String name;
        Set<Product> products;
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
        @JoinTable(
                name="user_product",
                joinColumns=@JoinColumn(name="uid"),
                inverseJoinColumns=@JoinColumn(name="pid")
        )
        public Set<Product> getProducts() {
            return products;
        }
        public void setProducts(Set<Product> products) {
            this.products = products;
        }
    }
    View Code

    b、添加hibernate.cfg.xml配置文件

    在src目录下创建 hibernate.cfg.xml
    配置访问数据库要用到的驱动,url,账号密码等等

    <?xml version='1.0' encoding='utf-8'?>
    <!DOCTYPE hibernate-configuration PUBLIC
            "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    
    <hibernate-configuration>
    
        <session-factory>
            <!-- Database connection settings -->
            <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
            <property name="connection.url">jdbc:mysql://192.168.80.128:3306/test?characterEncoding=UTF-8</property>
            <property name="connection.username">root</property>
            <property name="connection.password">111111</property>
            <!-- SQL dialect -->
            <!--这表示使用MYSQL方言。 什么方言呢? 因为在代码层面,开发人员不用关心底层到底用Oracle还是Mysql,写的代码都是一样的。
            可是Oracle和Mysql所用的sql语句的语法是有所区别的,那么这件事就交给Hibernate来做了。
            这个时候就需要告诉Hibernate底层用的是什么数据库,它才知道应该用什么样的“方言” 去对话。-->
            <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
            <!--这是Hibernate事务管理方式,即每个线程一个事务-->
            <property name="current_session_context_class">thread</property>
            <!--这表示是否在控制台显示执行的sql语句-->
            <property name="show_sql">true</property>
            <!--这表示是否会自动更新数据库的表结构,有这句话,其实是不需要创建表的,因为Hibernate会自动去创建表结构-->
            <property name="hbm2ddl.auto">update</property>
            <!--    <mapping resource="com/how2java/pojo/Product.hbm.xml" /> -->
            <!--这表示Hibernate会去识别Product、category、user这些实体类-->
            <mapping class="pojo.Product" />
            <mapping class="pojo.Category" />
            <mapping class="pojo.User" />
    
        </session-factory>
    
    </hibernate-configuration>
    View Code

    二、基本操作

    a、插入数据到数据库

    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
     
    import com.how2java.pojo.Product;
     
    public class TestHibernate {
        public static void main(String[] args) {
     
            SessionFactory sf = new Configuration().configure().buildSessionFactory();
     
            Session s = sf.openSession();
            s.beginTransaction();
     
            for (int i = 0; i < 10; i++) {
                Product p = new Product();
                p.setName("iphone"+i);
                p.setPrice(i);
                s.save(p);         
            }
     
            s.getTransaction().commit();
            s.close();
            sf.close();
        }
     
    }
    View Code

    b、对象的三种状态 瞬时 持久 脱管

    瞬时 指的是没有和hibernate发生任何关系,在数据库中也没有对应的记录,一旦JVM结束,这个对象也就消失了 
    持久 指得是一个对象和hibernate发生联系,有对应的session,并且在数据库中有对应的一条记录 
    脱管 指的是一个对象虽然在数据库中有对应的一条记录,但是它所对应的session已经关闭了 

            SessionFactory sf = new Configuration().configure().buildSessionFactory();
      
            Session s = sf.openSession();
            s.beginTransaction();
            Product p = new Product();
            p.setName("p1");
            System.out.println("此时p是瞬时状态");
            s.save(p);
            System.out.println("此时p是持久状态");
            s.getTransaction().commit();
            s.close();
            System.out.println("此时p是脱管状态");
            sf.close();
    View Code

    c、获取

    调用Session的get方法,根据id获取对象。 除了id之外,还需要传递一个类对象,毕竟需要知道获取的是哪个对象。

    除了使用get方法,还可以使用load获取对象。

            SessionFactory sf = new Configuration().configure().buildSessionFactory();
      
            Session s = sf.openSession();
            s.beginTransaction();
      
            Product p =(Product) s.get(Product.class, 6);
              
            System.out.println("id=6的产品名称是: "+p.getName());
              
            s.getTransaction().commit();
            s.close();
            sf.close();
    View Code

    d、删除

    根据id把对象从表里删除掉 

    注意:hibernate在删除一条数据之前,先要通过id把这条记录取出来
            SessionFactory sf = new Configuration().configure().buildSessionFactory();
     
            Session s = sf.openSession();
            s.beginTransaction();
     
            Product p =(Product) s.get(Product.class, 5);
            s.delete(p);
             
            s.getTransaction().commit();
            s.close();
            sf.close();
    View Code

    e、修改

    1. 根据id获取该对象
    2. 修改该对象的属性
    3. 通过Session的update方法把变化更新到数据库中

            SessionFactory sf = new Configuration().configure().buildSessionFactory();
     
            Session s = sf.openSession();
            s.beginTransaction();
     
            Product p =(Product) s.get(Product.class, 6);
             
            System.out.println(p.getName());
             
            p.setName("iphone-modified");
             
            s.update(p);
             
            s.getTransaction().commit();
            s.close();
            sf.close();
    View Code

    三、三种查询语言Hql Criteria sql

    a、HQL(Hibernate Query Language)

    是hibernate专门用于查询数据的语句,有别于SQL,HQL 更接近于面向对象的思维方式。 
    比如使用的是类的名字Product,而非表格的名字product_

    1. 首先根据hql创建一个Query对象
    2. 设置参数
    3. 通过Query对象的list()方法即返回查询的结果了。

    注: 使用hql的时候,用的是类名Product,而不是表名product_
    注: 使用hql的时候,不需要在前面加 select *

            SessionFactory sf = new Configuration().configure().buildSessionFactory();
     
            Session s = sf.openSession();
            s.beginTransaction();
     
            String name = "iphone";
            Query q =s.createQuery("from Product p where p.name like ?");
            q.setString(0, "%"+name+"%");
            List<Product> ps= q.list();
            for (Product p : ps) {
                System.out.println(p.getName());
            }
             
            s.getTransaction().commit();
            s.close();
            sf.close();
    View Code

    b、Criteria

    与HQL和SQL的区别是Criteria 完全是 面向对象的方式在进行数据查询,将不再看到有sql语句的痕迹

    1. 通过session的createCriteria创建一个Criteria 对象
    2. Criteria.add 增加约束。 在本例中增加一个对name的模糊查询(like)
    3. 调用list()方法返回查询结果的集合

            SessionFactory sf = new Configuration().configure().buildSessionFactory();
      
            Session s = sf.openSession();
            s.beginTransaction();
     
            String name = "iphone";
              
            Criteria c= s.createCriteria(Product.class);
            c.add(Restrictions.like("name", "%"+name+"%"));
            List<Product> ps = c.list();
            for (Product p : ps) {
                System.out.println(p.getName());
            }
            s.getTransaction().commit();
            s.close();
            sf.close();
    View Code

    c、标准sql

    Hibernate依然保留了对标准SQL语句的支持,在一些场合,比如多表联合查询,并且有分组统计函数的情况下,标准SQL语句依然是效率较高的一种选择

    使用Session的createSQLQuery方法执行标准SQL语句
    因为标准SQL语句有可能返回各种各样的结果,比如多表查询,分组统计结果等等。 不能保证其查询结果能够装进一个Product对象中,所以返回的集合里的每一个元素是一个对象数组。 然后再通过下标把这个对象数组中的数据取出来。

            SessionFactory sf = new Configuration().configure().buildSessionFactory();
     
            Session s = sf.openSession();
            s.beginTransaction();
     
            String name = "iphone";
             
            String sql = "select * from product_ p where p.name like '%"+name+"%'";
             
            Query q= s.createSQLQuery(sql);
            List<Object[]> list= q.list();
            for (Object[] os : list) {
                for (Object filed: os) {
                    System.out.print(filed+"	");
                }
                System.out.println();
            }
             
            s.getTransaction().commit();
            s.close();
            sf.close();
    View Code

     四、关系

    a、多对一

    一个Product对应一个Category 
    一个Category对应多个Product 
    所以Product和Category是多对一的关系

    把Product的getCategory进行多对一映射

        @ManyToOne
        @JoinColumn(name="cid")
        public Category getCategory() {
            return category;
        }
    
        public void setCategory(Category category) {
            this.category = category;
        }
    View Code
    View Code

    b、一对多

    一个Product对应一个Category 
    一个Category对应多个Product 
    所以Category和Product是一对多的关系 

    给getProducts方法加上一对多注解

        @OneToMany(fetch=FetchType.EAGER)
        @JoinColumn(name="cid")
        public Set<Product> getProducts() {
            return products;
        }
    
        public void setProducts(Set<Product> products) {
            this.products = products;
        }
    View Code
            SessionFactory sf = new Configuration().configure().buildSessionFactory();
      
            Session s = sf.openSession();
            s.beginTransaction();
             
            Category c = (Category) s.get(Category.class, 1);
            Set<Product> ps = c.getProducts();
            for (Product p : ps) {
                System.out.println(p.getName());
            }
     
            s.getTransaction().commit();
            s.close();
            sf.close();
    View Code

    c、多对多

    一种Product可以被多个User购买
    一个User可以购买多种Product

    所以Product和User之间的关系是多对多 many-to-many
    要实现多对多关系,必须有一张中间表 user_product 用于维护 User和Product之间的关系

    1. 加上多对一注解ManyToOne
    2. 加上一对多注解OneToMany
    3. ManyToMany
    为Product的getUsers加上

              @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
        @JoinTable(
                name="user_product",
                joinColumns=@JoinColumn(name="uid"),
                inverseJoinColumns=@JoinColumn(name="pid")
        )
        public Set<Product> getProducts() {
            return products;
        }
        public void setProducts(Set<Product> products) {
            this.products = products;
        }
    View Code
            SessionFactory sf = new Configuration().configure().buildSessionFactory();
       
            Session s = sf.openSession();
            s.beginTransaction();
              
            //增加3个用户
            Set<User> users = new HashSet();
            for (int i = 0; i < 3; i++) {
                User u =new User();
                u.setName("user"+i);
                users.add(u);
                s.save(u);
            }
              
            //产品1被用户1,2,3购买
            Product p1 = (Product) s.get(Product.class, 1);
              
            p1.setUsers(users);
            s.save(p1);
            s.getTransaction().commit();
            s.close();
            sf.close();
    View Code

     五、注解手册

    a、类相关注解

    @Entity —— 将一个类声明为一个实体bean(即一个持久化POJO类) 
    @Table —— 注解声明了该实体bean映射指定的表(table),目录(catalog)和schema的名字

    b、属性相关注解

    @Id —— 注解声明了该实体bean的标识属性(对应表中的主键)。
    @Column —— 注解声明了属性到列的映射。该注解有如下的属性
      name 可选,列名(默认值是属性名)
      unique 可选,是否在该列上设置唯一约束(默认值false)
      nullable 可选,是否设置该列的值可以为空(默认值false)
      insertable 可选,该列是否作为生成的insert语句中的一个列(默认值true)
      updatable 可选,该列是否作为生成的update语句中的一个列(默认值true)
      columnDefinition 可选,为这个特定列覆盖sql ddl片段(这可能导致无法在不同数据库间移植)
      table 可选,定义对应的表(默认为主表)
      length 可选,列长度(默认值255)
      precision 可选,列十进制精度(decimal precision)(默认值0)
      scale 可选,如果列十进制数值范围(decimal scale)可用,在此设置(默认值0)
    @GeneratedValue —— 注解声明了主键的生成策略。该注解有如下属性
      strategy 指定生成的策略(JPA定义的),这是一个GenerationType。默认是GenerationType. AUTO
      GenerationType.AUTO 主键由程序控制
      GenerationType.TABLE 使用一个特定的数据库表格来保存主键
      GenerationType.IDENTITY 主键由数据库自动生成(主要是自动增长类型)
      GenerationType.SEQUENCE 根据底层数据库的序列来生成主键,条件是数据库支持序列。(这个值要与generator一起使用)
      generator 指定生成主键使用的生成器(可能是orcale中的序列)。
    @SequenceGenerator —— 注解声明了一个数据库序列。该注解有如下属性
      name 表示该表主键生成策略名称,它被引用在@GeneratedValue中设置的“gernerator”值中
      sequenceName 表示生成策略用到的数据库序列名称。
      initialValue 表示主键初始值,默认为0.
      allocationSize 每次主键值增加的大小,例如设置成1,则表示每次创建新记录后自动加1,默认为50.

    c、关系相关注解

    @ManyToOne 设置多对一关联 
    方法一 
      @ManyToOne(cascade={CasCadeType.PERSIST,CascadeType.MERGE}) 
      @JoinColumn(name="外键") 
      public 主表类 get主表类(){return 主表对象} 
    方法二 
      @ManyToOne(cascade={CascadeType.PERSIST,CascadeType.MERGE}) 
      @JoinTable(name="关联表名", 
      joinColumns = @JoinColumn(name="主表外键"), 
      inverseJoinColumns = @JoinColumns(name="从表外键") 
      ) 
    @OneToMany 设置一对多关联。
    方法一 。 
    “一端”配置 
      @OneToMany(mappedBy="“多端”的属性") 
      public List<“多端”类> get“多端”列表(){return “多端”列表} 
      “多端”配置参考@ManyToOne. 
    方法二 
      “一端”配置 
      @OneToMany(mappedBy="“多端”的属性") 
      @MapKey(name="“多端”做为Key的属性") 
      public Map<“多端”做为Key的属性的类,主表类> get“多端”列表(){return “多端”列表} 
      “多端”配置参考@ManyToOne. 
    方法三 使用这种配置,在为“一端”添加“多端”时,可以修改“多端”的外键。 
      “一端”配置 
      @OneToMany 
      @JoinColumn(name="“多端”外键") 
      public List<“多端”类> get“多端”列表(){return “多端”列表} 
      “多端”配置参考@ManyToOne.

    d、注解 vs XML

    那么到底该用注解,还是xml文件配置方式呢? 他们各自有各自的优缺点: 

    XML配置方式: 
      优:容易编辑,配置比较集中,方便修改,在大业务量的系统里面,通过xml配置会方便后人理解整个系统的架构,修改之后直接重启应用即可 
      缺:比较繁琐,配置形态丑陋, 配置文件过多的时候难以管理 
    注解方式: 
      优:方便,简洁,配置信息和 Java 代码放在一起,有助于增强程序的内聚性。 
      缺:分散到各个class文件中,所以不宜维护, 修改之后你需要重新打包,发布,重启应用。 

    个人体会: 小项目,参与人数不多,不复杂的用注解,开发快速。 复杂项目,多人交互,配置量大,维护复杂度高的,用配置文件。

    六、相关概念

    a、事务

    Hibernate的任何对数据有改动的操作,都应该被放在事务里面. 
    在事务中的多个操作行为,要么都成功,要么都失败

            SessionFactory sf = new Configuration().configure().buildSessionFactory();
     
            Session s = sf.openSession();
            s.beginTransaction();
     
            Product p = (Product) s.get(Product.class, 1);
            s.delete(p);
     
            Product p2 = (Product) s.get(Product.class, 2);
            p2.setName("长度超过30的字符串作为产品名称长度超过30的字符串作为产品名称长度超过30的字符串作为产品名称长度超过30的字符串作为产品名称");
            s.update(p2);
     
            s.getTransaction().commit();
            s.close();
            sf.close();
    View Code

    在Mysql中,只有当表的类型是INNODB的时候,才支持事务,所以需要把表的类型设置为INNODB,否则无法观察到事务.

    b、属性延迟加载

    属性的延迟加载: 
    当使用load的方式来获取对象的时候,只有访问了这个对象的属性,hibernate才会到数据库中进行查询。否则不会访问数据库。

            SessionFactory sf = new Configuration().configure().buildSessionFactory();
      
            Session s = sf.openSession();
            s.beginTransaction();
         
            Product p = (Product)s.load(Product.class, 1);
            System.out.println("log1");
            System.out.println(p.getName());
            System.out.println("log2");
     
            s.getTransaction().commit();
            s.close();
            sf.close();
    View Code

    c、关系延迟加载

    延迟加载又叫lazyload,在one-many many-many的时候都可以使用关系的延迟加载

            SessionFactory sf = new Configuration().configure().buildSessionFactory();
     
            Session s = sf.openSession();
            s.beginTransaction();
            Category c = (Category) s.get(Category.class, 1);
     
            System.out.println("log1");
            System.out.println(c.getProducts());
            System.out.println("log1");
     
            s.getTransaction().commit();
            s.close();
            sf.close();
    View Code

    System.out.println(c.getProducts());

    只有在执行上面这行,通过category取products的时候,才会进行对product_表的查询

    d、级联

    什么是级联? 简单的说,没有配置级联的时候,删除分类,其对应的产品不会被删除。 但是如果配置了恰当的级联,那么删除分类的时候,其对应的产品都会被删除掉。

    包括上一步说的删除用得级联,级联有4种类型:
    all:所有操作都执行级联操作;
    none:所有操作都不执行级联操作;
    delete:删除时执行级联操作; 
    save-update:保存和更新时执行级联操作;
    级联通常用在one-many和many-to-many上,几乎不用在many-one上。

    为Category.hbm.xml 加上 

    <set name="products" cascade="delete" lazy="false">
    View Code

    运行代码就会发现,删除分类的时候,会把分类下对应的产品都删除掉,否则只会把产品对应的cid设置为空

    e、一级缓存

    hibernate默认是开启一级缓存的,一级缓存存放在session上

            SessionFactory sf = new Configuration().configure().buildSessionFactory();
       
            Session s = sf.openSession();
            s.beginTransaction();
            System.out.println("log1");
            Category c1 = (Category)s.get(Category.class, 1);
            System.out.println("log2");
            Category c2= (Category)s.get(Category.class, 1);
            System.out.println("log3");       
     
            s.getTransaction().commit();
            s.close();
            sf.close();
    View Code

    第一次通过id=1获取对象的时候,session中是没有对应缓存对象的,所以会在"log1"后出现sql查询语句。
    第二次通过id=1获取对象的时候,session中有对应的缓存对象,所以在"log2"后不会出现sql查询语句

    所以总共会看到一次SQL语句出现

    f、二级缓存

    Hibernate的一级缓存是在Session上,二级缓存是在SessionFactory上

    第一步:在hibernate.cfg.xml中开启二级缓存的配置
    hibernate本身不提供二级缓存,都是使用第三方的二级缓存插件
    这里使用的是 EhCache提供的二级缓存

            <property name="hibernate.cache.use_second_level_cache">true</property>
            <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
    View Code

    第二步:

    在src目录下,创建一个ehcache.xml用于EHCache的缓存配置

    <ehcache>
        <diskStore path="java.io.tmpdir"/>
        <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            />
    </ehcache>
    View Code

    第三步:

    对于要进行二级缓存的实体类,进行配置,增加
    <cache usage="read-only" />

    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC
            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
     
    <hibernate-mapping package="pojo">
        <class name="Category" table="category_">
            <cache usage="read-only" />
            <id name="id" column="id">
                <generator class="native">
                </generator>
            </id>
            <property name="name" />
     
            <set name="products" lazy="true">
                <key column="cid" not-null="false" />
                <one-to-many class="Product" />
            </set>
                     
        </class>
         
    </hibernate-mapping>
    View Code

    测试:

    使用不同的session,都去获取id=1的category,只会访问一次数据库。因为第二次获取虽然没有从第二个session中拿到缓存,但是从sessionfactory中拿到了Category缓存对象

            SessionFactory sf = new Configuration().configure().buildSessionFactory();
            Session s = sf.openSession();
            s.beginTransaction();
             
            Category c1 = (Category)s.get(Category.class, 1);
            System.out.println("log1");
            Category c2= (Category)s.get(Category.class, 1);
            System.out.println("log2");
            s.getTransaction().commit();
            s.close();
            Session s2 = sf.openSession();
            s2.beginTransaction();
            Category c3 = (Category)s2.get(Category.class, 1);
            System.out.println("log3");
      
            s2.getTransaction().commit();
            s2.close();
            sf.close();
    View Code

    g、分页

    使用Criteria进行分页查询 
    无论你使用的是Oracle,Mysql,NoSQL还是DB2,分页查询的代码写法都是一样的

    c.setFirstResult(2); 表示从第3条数据开始
    c.setMaxResults(5); 表示一共查询5条数据

            SessionFactory sf = new Configuration().configure().buildSessionFactory();
      
            Session s = sf.openSession();
            s.beginTransaction();
      
            String name = "iphone";
              
            Criteria c= s.createCriteria(Product.class);
            c.add(Restrictions.like("name", "%"+name+"%"));
            c.setFirstResult(2);
            c.setMaxResults(5);
             
            List<Product> ps = c.list();
            for (Product p : ps) {
                System.out.println(p.getName());
                 
            }
              
            s.getTransaction().commit();
            s.close();
            sf.close();
    View Code

    h、两种获取方式,get和load

    通过id获取Product对象有两种方式,分别是get和load 
    他们的区别分别在于 
     延迟加载 

    load方式是延迟加载,只有属性被访问的时候才会调用sql语句
    get方式是非延迟加载,无论后面的代码是否会访问到属性,马上执行sql语句

    对于id不存在的时候的处理

    1. get方式会返回null 
    2. load方式会抛出异常

    i、两种session方式,openSession和getCurrentSession 

    他们的区别在于 
    1. 获取的是否是同一个session对象 
        openSession每次都会得到一个新的Session对象 
        getCurrentSession在同一个线程中,每次都是获取相同的Session对象,但是在不同的线程中获取的是不同的Session对象 
    2. 事务提交的必要性 
        openSession只有在增加,删除,修改的时候需要事务,查询时不需要的 
        getCurrentSession是所有操作都必须放在事务中进行,并且提交事务后,session就自动关闭,不能够再进行关闭 

    j、N+1

    Hibernate有缓存机制,可以通过用id作为key把product对象保存在缓存中 

    同时hibernate也提供Query的查询方式。假设数据库中有100条记录,其中有30条记录在缓存中,但是使用Query的list方法,就会所有的100条数据都从数据库中查询,而无视这30条缓存中的记录 

    N+1是什么意思呢,首先执行一条sql语句,去查询这100条记录,但是,只返回这100条记录的ID 
    然后再根据id,进行进一步查询。 

    如果id在缓存中,就从缓存中获取product对象了,否则再从数据库中获取

    k、查询总数

    调用Query对象的uniqueResult()方法,返回一个long型的数据,即查询总数。

            SessionFactory sf = new Configuration().configure().buildSessionFactory();
            Session s = sf.openSession();
            s.beginTransaction();
      
            String name = "iphone";
             
            Query q =s.createQuery("select count(*) from Product p where p.name like ?");
            q.setString(0, "%"+name+"%");
            long total= (Long) q.uniqueResult();
            System.out.println(total);
            s.getTransaction().commit();
            s.close();
            sf.close();
    View Code

    l、乐观锁

    Hibernate使用乐观锁来处理脏数据问题

    第一步:修改配置文件 Product.hbm.xml

    <version name="version" column="ver" type="int"></version>
     

    增加一个version字段,用于版本信息控制。这就是乐观锁的核心机制。
    比如session1获取product1的时候,version=1。 那么session1更新product1的时候,就需要确保version还是1才可以进行更新,并且更新结束后,把version改为2。
    注意: version元素必须紧跟着id后面,否则会出错。

    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC
            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
     
    <hibernate-mapping package="com.how2java.pojo">
        <class name="Product" table="product_">
            <id name="id" column="id">
                <generator class="native">
                </generator>
            </id>
            <!--version元素必须紧挨着id后面  -->
            <version name="version" column="ver" type="int"></version>
            <property name="name" />
            <property name="price" />
     
            <many-to-one name="category" class="Category" column="cid" />
     
            <set name="users" table="user_product" lazy="false">
                <key column="pid" />
                <many-to-many column="uid" class="User" />
            </set>
     
        </class>
     
    </hibernate-mapping>
    View Code

    第二步:修改Product.java

    增加version属性

    第三步:运行下面的代码

    做同样的业务就会抛出异常,提示该行已经被其他事务删除或者修改过了,本次修改无法生效。

    这样就保证了数据的一致性。

            SessionFactory sf = new Configuration().configure().buildSessionFactory();
            Session s1 = sf.openSession();
            Session s2 = sf.openSession();
     
            s1.beginTransaction();
            s2.beginTransaction();
     
            Product p1 = (Product) s1.get(Product.class, 1);
            System.out.println("产品原本价格是: " + p1.getPrice());
     
            p1.setPrice(p1.getPrice() + 1000);
     
            Product p2 = (Product) s2.get(Product.class, 1);
            p2.setPrice(p2.getPrice() + 1000);
     
            s1.update(p1);
            s2.update(p2);
     
            s1.getTransaction().commit();
            s2.getTransaction().commit();
     
            Product p = (Product) s1.get(Product.class, 1);
     
            System.out.println("经过两次价格增加后,价格变为: " + p.getPrice());
     
            s1.close();
            s2.close();
     
            sf.close();
    View Code

    原理:

    1. 假设数据库中产品的价格是10000,version是10
    2. session1,session2分别获取了该对象
    3. 都修改了对象的价格
    4. session1试图保存到数据库,检测version依旧=10,成功保存,并把version修改为11
    5. session2试图保存到数据库,检测version=11,说明该数据已经被其他人动过了。 保存失败,抛出异常

  • 相关阅读:
    WebService协议
    用实例揭示notify()和notifyAll()的本质区别 收藏
    深入Java集合学习系列:HashMap的实现原理
    Oracle 索引扫描的五种类型
    Spring 异常
    Spring MVC
    银行家算法
    Java内存模型与多线程
    Spring MVC之@RequestParam @RequestBody @RequestHeader 等详解
    SpringMVC单元测试之MockMVC,模拟登入用户
  • 原文地址:https://www.cnblogs.com/afeng2010/p/10081508.html
Copyright © 2011-2022 走看看