Hibernate简洁教程

Hibernate

基础

简单使用

准备数据表

use test;
 
CREATE TABLE product_ (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(30) ,
  price float ,
  PRIMARY KEY (id)
) DEFAULT CHARSET=UTF8;

创建项目

引入pom文件:

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.4.2.Final</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

创建实体类

package pojo;
public class Product {
    int id;
    String name;
    float price;
    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;
    }
    public float getPrice() {
        return price;
    }
    public void setPrice(float price) {
        this.price = price;
    }
}

配置 Product.hbm.xml

新建一个配置文件Product.hbm.xml, 用于映射Product类对应数据库中的product_表
注意:名字要和类保持一致

<?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">
    <!--表示类Product对应表product_-->
    <class name="Product" table="product_">
        <id name="id" column="id">
            <!--意味着id的自增长方式采用数据库的本地方式-->
           <!-- <generator class="native">
            </generator>-->
        </id>
        <property name="name" />
        <property name="price" />
    </class>

</hibernate-mapping>

配置 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://localhost:3306/test?characterEncoding=UTF-8</property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>
        <!-- SQL dialect -->
        <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="Product.hbm.xml" />
    </session-factory>

</hibernate-configuration>

测试类

public class TestHibernate {

    @Test
    public void test1(){
        /*获取SessionFactory */
        SessionFactory sf = new Configuration().configure().buildSessionFactory();

        /*通过SessionFactory 获取一个Session*/
        Session s = sf.openSession();

        /*在Session基础上开启一个事务*/
        s.beginTransaction();

        Product p = new Product();
        p.setName("iphone7");
        p.setPrice(7000);

        /*通过调用Session的save方法把对象保存到数据库*/
        s.save(p);

        /*提交事务*/
        s.getTransaction().commit();

        /*关闭Session*/
        s.close();
        
        /*关闭SessionFactory*/
        sf.close();
    }
}

循环插入数据

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.junit.Test;
import pojo.Product;

public class TestHibernate {

    @Test
    public void test1() {
        /*获取SessionFactory */
        SessionFactory sf = new Configuration().configure().buildSessionFactory();

        /*通过SessionFactory 获取一个Session*/
        Session s = sf.openSession();

        /*在Session基础上开启一个事务*/
        s.beginTransaction();

        for (int i = 0; i <= 10; i++) {
            Product p = new Product();
            p.setName("iphone"+i);
            p.setPrice(i*1000);
            /*通过调用Session的save方法把对象保存到数据库*/
            s.save(p);
            
            /*如果没有清空session,那么肯定会报错。因为这样会产生许多相同的id*/
            s.flush();
            s.clear();
        }
        /*提交事务*/
        s.getTransaction().commit();

        /*关闭Session*/
        s.close();

        /*关闭SessionFactory*/
        sf.close();
    }
}

对象状态

       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();
  • new 了一个Product();,在数据库中还没有对应的记录,这个时候Product对象的状态是瞬时的。
  • 通过Session的save把该对象保存在了数据库中,该对象也和Session之间产生了联系,此时状态是持久的。
  • 最后把Session关闭了,这个对象在数据库中虽然有对应的数据,但是已经和Session失去了联系,相当于脱离了管理,状态就是脱管的

获取对象

    @Test
    public void getTest() {
        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
        Session session = sessionFactory.openSession();
        Product product = session.get(Product.class, 1);
        System.out.println(product);
        session.close();
        sessionFactory.close();
    }

删除对象

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

更新对象

    @Test
    public void updateTest() {
        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
        Session session = sessionFactory.openSession();
        session.getTransaction().begin();
        //获取一个对象
        Product product = session.get(Product.class, 1);
        product.setPrice(8000);
        //更新一个对象
        session.update(product);
        session.getTransaction().commit();
        session.close();
        sessionFactory.close();
    }

使用hql

    @Test
    public void hqlTest() {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        s.getTransaction().begin();
        /*首先根据hql创建一个Query对象*/
        Query q = s.createQuery("from Product p where p.name like ?1");
        /*设置参数*/
        q.setString(1, "%iphone%");
        /*通过Query对象的list()方法即返回查询的结果了。*/
        List<Product> list = q.list();
        for (Product p :
                list) {
            System.out.println(p);
        }
        s.getTransaction().commit();
        s.close();
        sf.close();
    }

Criteria

    @Test
    public void CriteriaTest() {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        s.getTransaction().begin();

        /*通过session的createCriteria创建一个Criteria 对象*/
        Criteria criteria=s.createCriteria("pojo.Product");

        /*Criteria.add 增加约束。 在本例中增加一个对name的模糊查询(like)*/
        criteria.add(Restrictions.like("name","%iphone%"));

        /*通过criteria对象的list()方法即返回查询的结果了。*/
        List<Product> list = criteria.list();
        for (Product p :
                list) {
            System.out.println(p);
        }
        s.getTransaction().commit();
        s.close();
        sf.close();
    }

使用SQL

    @Test
    public void sqlTest() {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        s.getTransaction().begin();
        /*SQL*/
        String sql = "select * from product_ p where p.name like '%iphone%'";

        Query q=s.createSQLQuery(sql);
        /*通过Query对象的list()方法即返回查询的结果了。*/
        List<Object[]> list= q.list();
        /*不能保证其查询结果能够装进一个Product对象中,所以返回的集合里的每一个元素是一个对象数组。 然后再通过下标把这个对象数组中的数据取出来。*/
        for (Object[] os : list) {
            for (Object filed: os) {
                System.out.print(filed+"\t");
            }
            System.out.println();
        }
        s.getTransaction().commit();
        s.close();
        sf.close();
    }

关系

实现多对一

数据库

product_表新增cid属性,关联category_表

Category类

package pojo;

public class Category {

    int id;
    String name;

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

    @Override
    public String toString() {
        return "Category{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

准备Category.hbm.xml

<?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_">
        <id name="id" column="id">
            <generator class="native"></generator>
        </id>
        <property name="name" column="name"/>
    </class>
</hibernate-mapping>

为Product.java增加Category属性

package pojo;

public class Product {
    int id;
    String name;
    float price;
    //增加Category属性
    Category category;


    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }


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

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", category=" + category +
                '}';
    }
}

在Product.hbm.xml中设置Category 多对一关系

<?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">
    <!--表示类Product对应表product_-->
    <class name="Product" table="product_">
        <id name="id" column="id">
            <!--意味着id的自增长方式采用数据库的本地方式-->
           <!-- <generator class="native">
            </generator>-->
        </id>
        <property name="name" />
        <property name="price" />
        <!--使用many-to-one 标签设置多对一关系-->
        <!--name="category" 对应Product类中的category属性
            class="Category" 表示对应Category类
            column="cid" 表示指向 category_表的外键-->
        <many-to-one name="category" column="cid" class="Category"/>
    </class>

</hibernate-mapping>

在hibernate.cfg.xml中增加Category的映射

        <!--新增Category映射-->
        <mapping resource="Category.hbm.xml"/>

TestHibernate 测试many-to-one关系

    @Test
    public void manytooneTest() {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        s.getTransaction().begin();
        Product p = s.get(Product.class, 1);
        System.out.println(p.toString());
        s.getTransaction().commit();
        s.close();
        sf.close();
    }

实现多对一

为Category增加一个Set集合

    Set<Product> products;
    public Set<Product> getProducts() {
        return products;
    }
    public void setProducts(Set<Product> products) {
        this.products = products;
    }

为Category.hbm.xml增加one-to-many映射

<?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_">
        <id name="id" column="id">
            <generator class="native"></generator>
        </id>
        <property name="name" column="name"/>
        <!--用于设置一对多(多对多也是他)关系,也可以用list-->
        <!--name="products" 对应 Category类中的products属性
            lazy="false" 表示不使用延迟加载。-->
        <set name="products" lazy="false">
            <!--表示外键是cid,可以为空-->
            <key column="cid" not-null="false"/>
            <!--表示一对多所对应的类是Product-->
            <one-to-many class="Product"/>
        </set>
    </class>
</hibernate-mapping>

TestHibernate 测试one-to-many关系

    @Test
    public void oneToManyTest() {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        s.getTransaction().begin();
        Category c = 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();
    }

实现多对多

数据库

新增user_表

新增user_product关联表

User实体类

package pojo;

import java.util.Set;

public class User {
    int id;
    String name;
    
    //产品
    Set<Product> products;

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

    public Set<Product> getProducts() {
        return products;
    }

    public void setProducts(Set<Product> products) {
        this.products = products;
    }
}

User.hbm.xml

<?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="User" table="user_">
        <id name="id" column="id">
            <generator class="native"></generator>
        </id>
        <property name="name" column="name"/>
        <set name="products" table="user_product" lazy="false">
            <key column="uid"></key>
            <many-to-many column="pid" class="Product"/>
        </set>
    </class>

</hibernate-mapping>

Product.java

增加了对应Users的集合

    Set<User> users;
 
    public Set<User> getUsers() {
        return users;
    }
    public void setUsers(Set<User> users) {
        this.users = users;
    }

Product.hbm.xml

<?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">
    <!--表示类Product对应表product_-->
    <class name="Product" table="product_">
        <id name="id" column="id">
            <!--意味着id的自增长方式采用数据库的本地方式-->
           <!-- <generator class="native">
            </generator>-->
        </id>
        <property name="name" />
        <property name="price" />
        <!--使用many-to-one 标签设置多对一关系-->
        <!--name="category" 对应Product类中的category属性
            class="Category" 表示对应Category类
            column="cid" 表示指向 category_表的外键-->
        <many-to-one name="category" column="cid" class="Category"/>


        <set name="users" table="user_product" lazy="false">
            <key column="pid"></key>
            <!--多对多关系-->
            <many-to-many column="uid" class="User"/>
        </set>
    </class>

</hibernate-mapping>

在hibernate.cfg.xml中增加User的映射

        <mapping resource="com/how2java/pojo/User.hbm.xml" />

测试many-to-many关系

    @Test
    public void manyToManyTest() {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        s.getTransaction().begin();
        User u = s.get(User.class, 1);
        Set<Product> productSet = u.getProducts();
        for (Product product : productSet
        ) {
            System.out.println(product);
        }
        s.getTransaction().commit();
        s.close();
        sf.close();
    }

注意:需要将product中的category从toString中移除,否则会报异常

概念

延迟加载

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

User.hbm.xml

<?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="User" table="user_">
        <id name="id" column="id">
            <generator class="native"></generator>
        </id>
        <property name="name" column="name"/>
        <!--非延迟加载-->
        <set name="products" table="user_product" lazy="false">
            <key column="uid"></key>
            <many-to-many column="pid" class="Product"/>
        </set>
    </class>

</hibernate-mapping>

测试

    //延迟测试
    @Test
    public void lazyTest(){
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        s.getTransaction().begin();
        User u = s.load(User.class, 1);
        System.out.println("~~~~~~~~~~~~~~~~~");
        System.out.println(u.getName());
        //Set<Product> products=u.getProducts();
        System.out.println("~~~~~~~~~~~~~~~~~");
        s.getTransaction().commit();
        s.close();
        sf.close();
    }

可以看到,直到我们调用get方法的时候,才会访问数据库。

修改User.hbm.xml

将Product设置为延迟加载,在看看运行效果

        <set name="products" table="user_product" lazy="true">
            <key column="uid"></key>
            <many-to-many column="pid" class="Product"/>
        </set>

可以看到,这里就不再对product进行数据库查询了,因为我们并不需要用到product

级联

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

4种级联

  • all:所有操作都执行级联操作;
  • none:所有操作都不执行级联操作;
  • delete:删除时执行级联操作;
  • save-update:保存和更新时执行级联操作;

级联通常用在one-many和many-to-many上,几乎不用在many-one上。

delete 级联

新增数据库记录

修改Category.hbm.xml
        <set name="products" cascade="delete" lazy="false">
            <!--表示外键是cid,可以为空-->
            <key column="cid" not-null="false"/>
            <!--表示一对多所对应的类是Product-->
            <one-to-many class="Product"/>
        </set>
创建一个新对象测试
    @Test
    public void deleteTest(){
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        s.getTransaction().begin();
        //创建一个类别对象,用于删除数据库中该类别
        Category c=new Category();
        c.setId(2);
        c.setName("iphone1");
        s.delete(c);
        s.getTransaction().commit();
        s.close();
        sf.close();
    }


可以看到,这里产品类并没有被删除,只是cid被清空

从数据库获取对象测试
    @Test
    public void deleteTest1() {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        s.getTransaction().begin();
        //从数据库获取对象
        Category c = s.load(Category.class, 2);
        s.delete(c);
        s.getTransaction().commit();
        s.close();
        sf.close();
    }

对于记录被删除

save-update 级联

修改Category.hbm.xml

<set name="products" cascade="save-update" lazy="false">

测试
    @Test
    public void test(){
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        s.beginTransaction();

        Category c = (Category) s.get(Category.class, 1);

        Product p1 = new Product();
        p1.setName("product_501");
        p1.setId(30);
        Product p2 = new Product();
        p2.setName("product_502");
        p2.setId(31);
        Product p3 = new Product();
        p3.setName("product_503");
        p3.setId(32);
        Set<Product> products=new HashSet<Product>();

        c.getProducts().add(p1);
        c.getProducts().add(p2);
        c.getProducts().add(p3);
        s.getTransaction().commit();
        s.close();
        sf.close();
    }

注意:使用此方式添加的数据,不会有自增长id,需要手动填写,否则会报错。

一级缓存

  • 第一次通过id=1获取对象的时候,session中是没有对应缓存对象的,所以会在"log"后出现sql查询语句。
  • 第二次通过id=1获取对象的时候,session中有对应的缓存对象,所以在"log"后不会出现sql查询语句
    @Test
    public void oneTest() {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        s.beginTransaction();
        Category c = s.get(Category.class, 1);
        System.out.println("log");
        Category c1 = s.get(Category.class, 1);
        s.getTransaction().commit();
        s.close();
        sf.close();
    }

二级缓存

  • 在第一个Session里
  • 第一次获取id=1的Category 会执行SQL语句
  • 第二次获取id=1的Category,不会执行SQL语句,因为有一级缓存
  • 在第二个Session里
  • 获取id=1的Category,会执行SQL语句,因为在第二个Session,没有缓存该对象。
    @Test
    public void twoTest(){
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        s.beginTransaction();
        Category p1 = (Category) s.get(Category.class, 1);
        System.out.println("一级缓存");
        Category p2 = (Category) s.get(Category.class, 1);
        s.getTransaction().commit();
        s.close();
        Session s2 = sf.openSession();
        s2.beginTransaction();
        System.out.println("二级缓存");
        Category p3 = (Category) s2.get(Category.class, 1);
        s2.getTransaction().commit();
        s2.close();
        sf.close();
    }

hibernate.cfg.xml 中增加对二级缓存的配置

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

<?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://localhost:3306/test?characterEncoding=UTF-8</property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>
        <!-- SQL dialect -->
        <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>

        <!--开启二级缓存-->
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

        <mapping resource="Product.hbm.xml" />
        <!--新增Category映射-->
        <mapping resource="Category.hbm.xml"/>
        <mapping resource="User.hbm.xml"/>
    </session-factory>

</hibernate-configuration>

ehcache.xml

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

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

设置HBM

对于要进行二级缓存的实体类,进行配置,增加

<?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="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>

实现分页查询

    @Test
    public void fenyueTest() {
        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 + "%"));
        //表示从第3条数据开始
        c.setFirstResult(2);
        //表示一共查询5条数据
        c.setMaxResults(5);

        List<Product> products = c.list();
        for (Product product :
                products) {
            System.out.println(product);
        }
        s.getTransaction().commit();
        s.close();
        sf.close();
    }

GET和LOAD的区别

延迟加载

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

对于id不存在的对象的处理

  • get方式会返回null
  • load方式会抛出异常

openSession和getCurrentSession的区别

openSession

  • OpenSession每次都会得到一个新的Session对象
  • openSession查询时候不需要事务:如果是做增加,修改,删除是必须放在事务里进行的。 但是如果是查询或者get,那么对于openSession而言就不需要放在事务中进行

getCurrentSession

  • 如果是同一个线程(本例是在主线程里),每次获取的都是相同的Session
  • 如果是不同线程,每次获取的都是不同的Session
  • getCurrentSession所有操作都必须放在事务中
  • getCurrentSession在提交事务后,session自动关闭 顶

N+1

N+1中的1,就是指只返回id的SQL语句,N指的是如果在缓存中找不到对应的数据,就到数据库中去查

    @Test
    public void N1Test() {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        s.beginTransaction();

        String name = "iphone";

        //首先通过Query的iterator把所有满足条件的Product的id查出来
        Query q = s.createQuery("from Product p where p.name like ?1");

        q.setString(1, "%" + name + "%");

        Iterator<Product> it = q.iterate();
        while (it.hasNext()) {
            //然后再通过it.next()查询每一个对象
            //如果这个对象在缓存中,就直接从缓存中取了
            //否则就从数据库中获取
            Product p = it.next();
            System.out.println(p.getName());
        }

        s.getTransaction().commit();
        s.close();
        sf.close();
    }

查询总数

        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        s.beginTransaction();
  
        String name = "iphone";
         
         //根据这条SQL语句创建一个Query对象,调用Query对象的uniqueResult()方法,返回一个long型的数据,即查询总数。
        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();

乐观锁

不使用乐观锁的情况

    @Test
    public void leguansuoTest(){
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s1 = sf.openSession();
        Session s2 = sf.openSession();

        s1.beginTransaction();
        s2.beginTransaction();

        //通过session1得到id=1的对象 product1
        Product p1 = (Product) s1.get(Product.class, 1);
        System.out.println("产品原本价格是: " + p1.getPrice());
        //在product1原来价格的基础上增加1000
        p1.setPrice(p1.getPrice() + 1000);

        //更新product1之前,通过session2得到id=1的对象product2
        Product p2 = (Product) s2.get(Product.class, 1);
        //在product2原来价格的基础上增加1000
        p2.setPrice(p2.getPrice() + 1000);

        //更新product1
        s1.update(p1);
        //更新product2
        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();
    }

最后结果是product的价格只增加了1000,而不是2000
实际上,这里的两次更新都是在8000基础上更新的,最终更新的结果就都是9000.

修改 Product.java

增加一个version字段,用于版本信息控制。这就是乐观锁的核心机制。
比如session1获取product1的时候,version=1。 那么session1更新product1的时候,就需要确保version还是1才可以进行更新,并且更新结束后,把version改为2。
增加version属性

    int version;
    public int getVersion() {
        return version;
    }
    public void setVersion(int version) {
        this.version = version;
    }

修改配置文件 Product.hbm.xml

<?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>

重新运行一样的代码

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

原理

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

注解

Hibernate的注解

使用注解之后不需要在使用hbm.xml文件配置映射关系。

常用注解

  • @Entity 表示这是一个实体类,用于映射表
  • @Table(name = "product_ ") 表示这是一个类,映射到的表名:product_
  • @Id 表示这是主键
  • @GeneratedValue(strategy = GenerationType.IDENTITY) 表示自增长方式使用mysql自带的
  • @Column(name = "id") 表示映射到字段id

修改Product类

package pojo;

import javax.persistence.*;
import java.util.Set;

//表示这是一个实体类,用于映射表
@Entity
//表示这是一个类,映射到的表名:product_
@Table(name = "product_")
public class Product {
    //表示这是主键
    @Id
    //表示映射到字段id
    @Column(name = "id")
    //表示自增长方式使用mysql自带的
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    int id;

    @Column(name = "name")
    String name;

    @Column(name = "price")
    float price;
    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;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }
    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

hibernate.cfg.xml 配置文件改动

<?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://localhost:3306/test?characterEncoding=UTF-8</property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>
        <!-- SQL dialect -->
        <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 class="pojo.Product"/>

<!--        <mapping resource="Product.hbm.xml" />
        &lt;!&ndash;新增Category映射&ndash;&gt;
        <mapping resource="Category.hbm.xml"/>
        <mapping resource="User.hbm.xml"/>-->
    </session-factory>

</hibernate-configuration>

多对一关系映射注解,一对多关系映射注解,多对多关系映射注解

多对一注解

  • @ManyToOne 表示多对一关系
  • @JoinColumn(name="cid") 表示关系字段是cid

Category类:

package com.how2java.pojo;
 
import javax.persistence.Column;
 
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
 
@Entity
@Table(name = "category_")
public class Category {
    int id;
    String name;
     
    @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;
    }
 
}

Product:

package com.how2java.pojo;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
  
@Entity
@Table(name = "product_")
public class Product {
    int id;
    String name;
    float price;
     
    Category category;
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")  
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @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;
    }
}

hibernate.cfg.xml

<?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://localhost:3306/test?characterEncoding=UTF-8</property>
        <property name="connection.characterEncoding">utf-8</property>
        <property name="connection.username">root</property>
        <property name="connection.password">admin</property>
        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="current_session_context_class">thread</property>
        <property name="show_sql">true</property>
        <property name="hbm2ddl.auto">update</property>
<!--    <mapping resource="com/how2java/pojo/Product.hbm.xml" /> -->
        <mapping class="com.how2java.pojo.Product" />
        <mapping class="com.how2java.pojo.Category" />
  
    </session-factory>
   
</hibernate-configuration>

一对多注解

  • @OneToMany 表示一对多,fetch=FetchType.EAGER 表示不进行延迟加载(FetchType.LAZY表示要进行延迟加载)
  • @JoinColumn(name="cid") 表示映射字段

Category

package com.how2java.pojo;
 
import java.util.Set;
 
import javax.persistence.Column;
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.OneToMany;
import javax.persistence.Table;
 
@Entity
@Table(name = "category_")
public class Category {
    int id;
    String name;
    Set<Product> 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;
    }
     
    @OneToMany(fetch=FetchType.EAGER)
    @JoinColumn(name="cid")
    public Set<Product> getProducts() {
        return products;
    }
    public void setProducts(Set<Product> products) {
        this.products = products;
    }
}

测试

package com.how2java.test;
   
import java.util.Set;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
import com.how2java.pojo.Category;
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();
        Category c = (Category) s.get(Category.class, 1);
        s.getTransaction().commit();
        s.close();
        sf.close();
        Set<Product> ps = c.getProducts();
        for (Product p : ps) {
            System.out.println(p.getName());
        }
    }
}

多对多

User

package com.how2java.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;
    }
}

Product

package com.how2java.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.ManyToOne;
import javax.persistence.Table;
  
@Entity
@Table(name="product_")
public class Product {
    int id;
    String name;
    float price;
    Category category;
    Set<User> users;
  
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    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;
    }
     
    @ManyToOne
    @JoinColumn(name="cid")    
    public Category getCategory() {
        return category;
    }
    public void setCategory(Category category) {
        this.category = category;
    }
     
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getPrice() {
        return price;
    }
    public void setPrice(float price) {
        this.price = price;
    }
      
}

Category

package com.how2java.pojo;
  
import java.util.Set;
 
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.OneToMany;
import javax.persistence.Table;
  
@Entity
@Table(name="category_")
public class Category {
    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;
    }
 
    @OneToMany(fetch=FetchType.EAGER)
    @JoinColumn(name="cid")    
    public Set<Product> getProducts() {
        return products;
    }
    public void setProducts(Set<Product> products) {
        this.products = products;
    }
}

测试

package com.how2java.test;
   
import java.util.HashSet;
import java.util.Set;
  
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
  
import com.how2java.pojo.Product;
import com.how2java.pojo.User;
   
public class TestHibernate {
    public static void main(String[] args) {
        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.getTransaction().commit();
        s.close();
        sf.close();
    }
}

posted on 2020-02-25 09:45  Mr.Tan&  阅读(...)  评论(...编辑  收藏

导航