Hibernate教程——我的笔记

Hibernate 简介

Hibernate 是由 Gavin King 于 2001 年创建的开放源代码的对象关系框架。它强大且高效的构建具有关系对象持久性和查询服务的 Java 应用程序。

Hibernate 将 Java 类映射到数据库表中,从 Java 数据类型中映射到 SQL 数据类型中,并把开发人员从 95% 的公共数据持续性编程工作中解放出来。

Hibernate 是传统 Java 对象和数据库服务器之间的桥梁,用来处理基于 O/R 映射机制和模式的那些对象。

在这里插入图片描述

Hibernate 优势

  • Hibernate 使用 XML 文件来处理映射 Java 类别到数据库表格中,并且不用编写任何代码。
  • 为在数据库中直接储存和检索 Java 对象提供简单的 APIs。
  • 如果在数据库中或任何其它表格中出现变化,那么仅需要改变 XML 文件属性。
  • 抽象不熟悉的 SQL 类型,并为我们提供工作中所熟悉的 Java 对象。
  • Hibernate 不需要应用程序服务器来操作。
  • 操控你数据库中对象复杂的关联。
  • 最小化与访问数据库的智能提取策略。
  • 提供简单的数据询问。

基础

一、第一个hibernate程序

1、本例演示如何使用hibernate往数据库插入一条数据。如图,这条iphone7的产品数据,就是由hibernate插入的

在这里插入图片描述

2、准备数据库test
create database test;
3、创建表
/*准备表product_, 有3个字段,分别是
主键id(自增长)
字符串格式的name
浮点数格式的price*/

use test;
 
CREATE TABLE product_ (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(30) ,
  price float ,
  PRIMARY KEY (id)
) DEFAULT CHARSET=UTF8;
4、创建一个maven project
5、导入hibernate所依赖的jar包
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.1.0.Final</version>
</dependency>

<!-- for JPA, use hibernate-entitymanager instead of hibernate-core -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>5.1.0.Final</version>
</dependency>

<!-- optional -->

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-osgi</artifactId>
    <version>5.1.0.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-envers</artifactId>
    <version>5.1.0.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-c3p0</artifactId>
    <version>5.1.0.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-proxool</artifactId>
    <version>5.1.0.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-infinispan</artifactId>
    <version>5.1.0.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-ehcache</artifactId>
    <version>5.1.0.Final</version>
</dependency>

在这里插入图片描述

6、创建实体类

实体类 Product 用于映射数据库中的表product_

package com.how2java.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;
    }
      
}
7、配置Product.hbm.xml

新建一个配置文件Product.hbm.xml, 用于映射Product类对应数据库中的product_表

注: 文件名 Product.hbm.xml P一定要大写,要和类保持一致

<?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">
<!--表示类Product对应表product_-->
    <class name="Product" table="product_">
    <!--表示属性id,映射表里的字段id
<generator class="native"> 意味着id的自增长方式采用数据库的本地方式

如果是连接oracle数据库,可以指定sequnce作为id自增长方式-->
        <id name="id" column="id">
            <generator class="native">
            </generator>
        </id>
        <!--只写了属性name,没有通过column="name" 显式的指定字段,那么字段的名字也是name.-->
        <property name="name" />
        <property name="price" />
    </class>
     
</hibernate-mapping>
8、配置 hibernate.cfg.xml

创建 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">admin</property>
        <!-- SQL dialect -->
        <!--这表示使用MYSQL方言-->
        <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>
        <!--表示Hibernate会去识别Product这个实体类-->
        <mapping resource="com/how2java/pojo/Product.hbm.xml" />
    </session-factory>
 
</hibernate-configuration>
9、测试类 TestHibernate
package com.how2java.test;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
import com.how2java.pojo.Product;
 /*
 创建一个Product对象,并通过hibernate把这个对象,插入到数据库中

hibernate的基本步骤是:
1. 获取SessionFactory 
2. 通过SessionFactory 获取一个Session
3. 在Session基础上开启一个事务
4. 通过调用Session的save方法把对象保存到数据库
5. 提交事务
6. 关闭Session
7. 关闭SessionFactory
 */
 
public class TestHibernate {
    public static void main(String[] args) {
 
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
 
        Session s = sf.openSession();
        s.beginTransaction();
 
        Product p = new Product();
        p.setName("iphone7");
        p.setPrice(7000);
        s.save(p);
         
        s.getTransaction().commit();
        s.close();
        sf.close();
    }
 
}
10、基本原理
应用程序通过Hibernate把 一个 Product对象插入到数据库的product_表中
hibernate.cfg.xml 配置文件提供链接数据库的基本信息
账号 密码 驱动 数据库ip 端口
Product.hbm.xml 提供对象与表的映射关系
对应哪个表? 什么属性,对应什么字段

在这里插入图片描述

二、插入

1、通过Hibernate可以很方便的批量插入数据到数据库中,这是插入10条数据的效果

在这里插入图片描述

2、使用Session的save方法插入数据。 这里插入多条数据,是为了后面学习删除,分页等功能做铺垫
package com.how2java.test;
 
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);
            //每次循环调用save
            s.save(p);         
        }
 
        s.getTransaction().commit();
        s.close();
        sf.close();
    }
 
}

三、对象状态

实体类对象在Hibernate中有3种状态

分别是瞬时,持久和脱管

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


new 了一个Product();,在数据库中还没有对应的记录,这个时候Product对象的状态是瞬时的。 

通过Session的save把该对象保存在了数据库中,该对象也和Session之间产生了联系,此时状态是持久的。

最后把Session关闭了,这个对象在数据库中虽然有对应的数据,但是已经和Session失去了联系,相当于脱离了管理,状态就是脱管的

演示

package com.how2java.test;
  
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
import com.how2java.pojo.Product;
  
public class TestHibernate {
    /**
     * @param args
     */
    public static void main(String[] args) {
  
        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();
    }
  
}

四、获取

通过id获取对象
package com.how2java.test;
  
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();
        
        //获取到id=6的产品的名称
        Product p =(Product) s.get(Product.class, 6);
          
        System.out.println("id=6的产品名称是: "+p.getName());
          
        s.getTransaction().commit();
        s.close();
        sf.close();
    }
  
}

结果:

在这里插入图片描述

五、删除

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

注意:hibernate在删除一条数据之前,先要通过id把这条记录取出来

package com.how2java.test;
 
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();
        
        //先取再删除
        Product p =(Product) s.get(Product.class, 5);
        s.delete(p);
         
        s.getTransaction().commit();
        s.close();
        sf.close();
    }
 
}

六、修改

通过Hibernate修改数据库中的数据。 把id=6的产品名称修改为 iphone-modifed

在这里插入图片描述

  1. 根据id获取该对象
  2. 修改该对象的属性
  3. 通过Session的update方法把变化更新到数据库中
package com.how2java.test;
 
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();
        
        //获取对象
        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();
    }
 
}

七、查询——hql

HQL(Hibernate Query Language)
是hibernate专门用于查询数据的语句,
有别于SQL,HQL 更接近于面向对象的思维方式。 

比如使用的是类的名字Product,而非表格的名字product_
这是通过Hibernate对所有数据查询的结果页面

在这里插入图片描述

package com.how2java.test;
 
import java.util.List;
 
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
import com.how2java.pojo.Product;
 
 /*
 1. 首先根据hql创建一个Query对象
2. 设置参数(和基1的PreparedStatement不一样,Query是基0的)
3. 通过Query对象的list()方法即返回查询的结果了。

注: 使用hql的时候,用的是类名Product,而不是表名product_
注: 使用hql的时候,不需要在前面加 select *
 */
 
public class TestHibernate {
    public static void main(String[] args) {
        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();
    }
 
}

八、查询——Criteria

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

在这里插入图片描述

package com.how2java.test;
  
import java.util.List;
 
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Restrictions;
 
import com.how2java.pojo.Product;
  
  /*
  使用Criteria 查询数据
1. 通过session的createCriteria创建一个Criteria 对象
2. Criteria.add 增加约束。 在本例中增加一个对name的模糊查询(like)
3. 调用list()方法返回查询结果的集合


除此之外,Criteria 还可以很方便的进行进行分页查询和获取总数
  */
  
public class TestHibernate {
    public static void main(String[] args) {
        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();
    }
}

九、查询——标准sql

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

在这里插入图片描述

package com.how2java.test;
 
import java.util.List;
 
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
 /*
 使用Session的createSQLQuery方法执行标准SQL语句

因为标准SQL语句有可能返回各种各样的结果,比如多表查询,分组统计结果等等。 不能保证其查询结果能够装进一个Product对象中,所以返回的集合里的每一个元素是一个对象数组。 然后再通过下标把这个对象数组中的数据取出来。
 */
 
public class TestHibernate {
    public static void main(String[] args) {
        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+"\t");
            }
            System.out.println();
        }
         
        s.getTransaction().commit();
        s.close();
        sf.close();
    }
}

关系

一、多对一

一个Product对应一个Category 
一个Category对应多个Product
所以Product和Category是多对一的关系
通过多对一关系,设置了id=8的产品对应 id=1的分类

在这里插入图片描述

1、准备Category.java
package com.how2java.pojo;

public class 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;
	}
	int id;
	String name;
}

2、准备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="com.how2java.pojo">
    <class name="Category" table="category_">
        <id name="id" column="id">
            <generator class="native">
            </generator>
        </id>
        <property name="name" />
    </class>
     
</hibernate-mapping>
3、为Product.java增加Category属性
package com.how2java.pojo;
public class Product {
    int id;
    String name;
    float price;
    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;
    }
     
}
4、在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="com.how2java.pojo">
    <class name="Product" table="product_">
        <id name="id" column="id">
            <generator class="native">
            </generator>
        </id>
 
        <property name="name" />
        <property name="price" />
        
        <!--
        name="category" 对应Product类中的category属性
class="Category" 表示对应Category类
column="cid" 表示指向 category_表的外键
-->
        <many-to-one name="category" class="Category" column="cid" />
    </class>
     
</hibernate-mapping>
5、在hibernate.cfg.xml中增加Category的映射
<?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=GBK</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 resource="com/how2java/pojo/Category.hbm.xml" />
    </session-factory>
 
</hibernate-configuration>
6、TestHibernate 测试many-to-one关系
package com.how2java.test;
  
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
import com.how2java.pojo.Category;
import com.how2java.pojo.Product;
 
 /*
 增加了一个新的Category对象"c1" 
并将其设置为id=8的product的category
 */
public class TestHibernate {
    public static void main(String[] args) {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
  
        Session s = sf.openSession();
        s.beginTransaction();
         
        Category c =new Category();
        c.setName("c1");
        s.save(c);
         
        Product p = (Product) s.get(Product.class, 8);
        p.setCategory(c);
        s.update(p);
         
        s.getTransaction().commit();
        s.close();
        sf.close();
    }
}

二、一对多

一个Product对应一个Category 
一个Category对应多个Product 

所以Category和Product是一对多的关系 
获取id=1分类下的所有商品

在这里插入图片描述

1、为Category增加一个Set集合
package com.how2java.pojo;
 
import java.util.Set;
 
public class 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;
    }
    int id;
    String name;
    Set<Product> products;
    public Set<Product> getProducts() {
        return products;
    }
    public void setProducts(Set<Product> products) {
        this.products = products;
    }
}
2、为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="com.how2java.pojo">
    <class name="Category" table="category_">
        <id name="id" column="id">
            <generator class="native">
            </generator>
        </id>
        <property name="name" />
        
        <!--
        <set 用于设置一对多(多对多也是他)关系,也可以用list,设置稍复杂点,这里使用简单的set来入门。
name="products" 对应 Category类中的products属性
lazy="false" 表示不使用延迟加载。关于延迟加载,请参考关系的延迟加载
<key column="cid" not-null="false" /> 表示外键是cid,可以为空
<one-to-many class="Product" /> 表示一对多所对应的类是Product
-->
        <set name="products" lazy="false">
            <key column="cid" not-null="false" />
            <one-to-many class="Product" />
        </set>
                 
    </class>
     
</hibernate-mapping>
3、TestHibernate 测试one-to-many关系
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;
  /*
  首先获取id=1的category,
  然后通过getProducts() 直接取出其所对应的所有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);
        Set<Product> ps = c.getProducts();
        for (Product p : ps) {
            System.out.println(p.getName());
        }
 
        s.getTransaction().commit();
        s.close();
        sf.close();
    }
}

三、多对多

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

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

User.java

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

Product.java
增加了对应Users的集合

package com.how2java.pojo;
 
import java.util.Set;
 
public class Product {
    int id;
    String name;
    float price;
    Category category;
    Set<User> users;
 
    public Set<User> getUsers() {
        return users;
    }
    public void setUsers(Set<User> users) {
        this.users = users;
    }
    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;
    }
     
}

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>
        <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>
在hibernate.cfg.xml中增加User的映射
<?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=GBK</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 resource="com/how2java/pojo/Category.hbm.xml" />
        <mapping resource="com/how2java/pojo/User.hbm.xml" />
    </session-factory>
 
</hibernate-configuration>
TestHibernate 测试many-to-many关系
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;
   /*
   首先增加3个用户
然后演示产品1被用户1,2,3购买

   */
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.save(p1);
        s.getTransaction().commit();
        s.close();
        sf.close();
    }
}

在这里插入图片描述

各类概念

一、事务

Hibernate的任何对数据有改动的操作,都应该被放在事务里面. 
在事务中的多个操作行为,要么都成功,要么都失败
  1. hibernate中的事务由s.beginTransaction();开始
  2. 由s.getTransaction().commit();结束
本例子,执行了两个操作
第一个,删除id=1的产品,这个是会成功的
第二个,修改id=2的产品,使得其产品名称超过了数据库中设置的长度30,这个是会失败的。
因为这两个操作都是在一个事务中,而且第二个操作失败了,所以最后的结果是两个操作都没有生效
package com.how2java.test;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.how2java.pojo.Product;
 
 /*
 在同一个事务中做了两件事情
1. 删除id=1的产品
2. 修改id=2的产品,使得其产品名称超过了数据库中设置的长度30.
提交事务。

第二个任务失败了,但是因为在同一个事务中,所以第一个任务也没有生效。因此id=1的产品,依然保留下来了。
 */
 
public class TestHibernate {
    public static void main(String[] args) {
        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();
    }
}

在这里插入图片描述

注:MYSQL 表的类型必须是INNODB才支持事务

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

修改表的类型为INNODB的SQL:
 
alter table product_ ENGINE  = innodb;
查看表的类型的SQL
 
show table status from test; 

不过有个前提,就是当前的MYSQL服务器本身要支持INNODB

二、属性延迟加载

hibernate中的延迟加载(lazyload)分属性的延迟加载和关系的延迟加载 

属性的延迟加载: 
当使用load的方式来获取对象的时候,只有访问了这个对象的属性,hibernate才会到数据库中进行查询。否则不会访问数据库
package com.how2java.test;
  
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
import com.how2java.pojo.Product;
  
  /*
  在打印log1之前,是不会打印出sql语句的,只有在访问属性“getName()"的时候,才会访问数据库
  */
  
public class TestHibernate {
    public static void main(String[] args) {
        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();
    }
}

在这里插入图片描述

三、关系延迟加载

延迟加载又叫lazyload,在one-many many-many的时候都可以使用关系的延迟加载
<set name="products" lazy="false">

改为

<set name="products" lazy="true">

表示通过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="com.how2java.pojo">
    <class name="Category" table="category_">
        <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>
package com.how2java.test;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
import com.how2java.pojo.Category;
 
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);
 
        System.out.println("log1");
        System.out.println(c.getProducts());
        System.out.println("log1");
 
        s.getTransaction().commit();
        s.close();
        sf.close();
    }
}

执行15行的时候,只会查询Category表的信息,不会查询product_表

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

在这里插入图片描述

三、级联

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

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

在一对多代码的基础之上,修改TestHibernate类为

Category c = (Category) s.get(Category.class, 3);
s.delete(c);

并且为Category.hbm.xml 加上

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

运行代码就会发现,删除分类的时候,会把分类下对应的产品都删除掉,否则只会把产品对应的cid设置为空
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="com.how2java.pojo">
    <class name="Category" table="category_">
        <id name="id" column="id">
            <generator class="native">
            </generator>
        </id>
        <property name="name" />
  
        <set name="products" cascade="delete" lazy="false">
            <key column="cid" not-null="false" />
            <one-to-many class="Product" />
        </set>
                  
    </class>
      
</hibernate-mapping>

TestHibernate.java

package com.how2java.test;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
import com.how2java.pojo.Category;
 
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, 3);
        s.delete(c);
        s.getTransaction().commit();
        s.close();
        sf.close();
    }
}

在这里插入图片描述

save-update 级联

在一对多代码的基础之上,修改TestHibernate类为

Category c = (Category) s.get(Category.class, 5);
Product p1 = new Product();
p1.setName("product_501");
Product p2 = new Product();
p2.setName("product_502");
Product p3 = new Product();
p3.setName("product_503");

并且为Category.hbm.xml 加上

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

运行代码就会发现,这3个瞬时状态的产品对象虽然没有添加到数据库里,但是在事务提交的时候,会把他们3个持久化。 如果没有cascade=“save-update”,就会报错

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="com.how2java.pojo">
    <class name="Category" table="category_">
        <id name="id" column="id">
            <generator class="native">
            </generator>
        </id>
        <property name="name" />
  
        <set name="products" cascade="save-update" lazy="false">
            <key column="cid" not-null="false" />
            <one-to-many class="Product" />
        </set>
                  
    </class>
      
</hibernate-mapping>

TestHibernate.java

package com.how2java.test;
 
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, 5);
         
        Product p1 = new Product();
        p1.setName("product_501");
        Product p2 = new Product();
        p2.setName("product_502");
        Product p3 = new Product();
        p3.setName("product_503");
 
        c.getProducts().add(p1);
        c.getProducts().add(p2);
        c.getProducts().add(p3);
 
        s.getTransaction().commit();
        s.close();
        sf.close();
    }
     
    public static void main4delete(String[] args) {
          SessionFactory sf = new Configuration().configure().buildSessionFactory();
            Session s = sf.openSession();
            s.beginTransaction();
            Category c = (Category) s.get(Category.class, 3);
            s.delete(c);
            s.getTransaction().commit();
            s.close();
            sf.close();
    }
}
all和none级联
all 就是 delete+save-update
none 就是没有,默认就是none

四、一级缓存

hibernate默认是开启一级缓存的,一级缓存存放在session上
package com.how2java.test;
   
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
  
import com.how2java.pojo.Category;
   /*
   第一次通过id=1获取对象的时候,session中是没有对应缓存对象的,所以会在"log1"后出现sql查询语句。
第二次通过id=1获取对象的时候,session中有对应的缓存对象,所以在"log2"后不会出现sql查询语句
   */
public class TestHibernate {
    public static void main(String[] args) {
        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();
    }
}

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

在这里插入图片描述

五、二级缓存

Hibernate的一级缓存是在Session上,二级缓存是在SessionFactory上
package com.how2java.test;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
import com.how2java.pojo.Category;
 /*
 创建了两个Session
在第一个Session里
第一次获取id=1的Category 会执行SQL语句
第二次获取id=1的Category,不会执行SQL语句,因为有一级缓存
在第二个Session里
获取id=1的Category,会执行SQL语句,因为在第二个Session,没有缓存该对象。 
所以总共会看到两条SQL语句。
 */
public class TestHibernate {
    public static void main(String[] args) {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
 
        Session s = sf.openSession();
        s.beginTransaction();
        Category p1 = (Category) s.get(Category.class, 1);
        Category p2 = (Category) s.get(Category.class, 1);
        s.getTransaction().commit();
        s.close();
        Session s2 = sf.openSession();
        s2.beginTransaction();
        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=GBK</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>
         
         <!--二级缓存-->
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
          
        <mapping resource="com/how2java/pojo/Product.hbm.xml" />
        <mapping resource="com/how2java/pojo/Category.hbm.xml" />
        <mapping resource="com/how2java/pojo/User.hbm.xml" />
         
    </session-factory>
 
</hibernate-configuration>
创建一个ehcache.xml用于EHCache的缓存配置
<ehcache>
    <diskStore path="java.io.tmpdir"/>
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />
</ehcache>
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="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>
测试效果
package com.how2java.test;
   
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
  
import com.how2java.pojo.Category;
   /*
   使用不同的session,都去获取id=1的category,只会访问一次数据库。
   因为第二次获取虽然没有从第二个session中拿到缓存,但是从sessionfactory中拿到了Category缓存对象
   */
public class TestHibernate {
    public static void main(String[] args) {
        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();
    }
}

在这里插入图片描述

七、分页

使用Criteria进行分页查询

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

package com.how2java.test;
  
import java.util.List;
 
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Restrictions;
 
import com.how2java.pojo.Product;
  /*
  Hibernate使用Criteria 来进行分页查询
c.setFirstResult(2); 表示从第3条数据开始
c.setMaxResults(5); 表示一共查询5条数据
  */
public class TestHibernate {
    public static void main(String[] args) {
        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();
    }
}

八、两种获取方式

通过id获取Product对象有两种方式,分别是get和load 
他们的区别分别在于 
1. 延迟加载 
2. 对于id不存在的时候的处理
延迟加载
package com.how2java.test;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
import com.how2java.pojo.Product;
 /*
 load方式是延迟加载,只有属性被访问的时候才会调用sql语句
get方式是非延迟加载,无论后面的代码是否会访问到属性,马上执行sql语句
 */
public class TestHibernate {
    public static void main(String[] args) {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
 
        Session s = sf.openSession();
        s.beginTransaction();
        System.out.println("log1");
        Product p = (Product) s.get(Product.class, 1);
        System.out.println("log2");
        Product p2 = (Product) s.load(Product.class, 2);
        System.out.println("log3");
        System.out.println(p2.getName());
        System.out.println("log4");
         
        s.getTransaction().commit();
        s.close();
        sf.close();
    }
}
对于id不存在的对象的处理
package com.how2java.test;
  
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
import com.how2java.pojo.Product;
  /*
  都通过id=500去获取对象 
1. get方式会返回null 
2. load方式会抛出异常
  */
public class TestHibernate {
    public static void main(String[] args) {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
  
        Session s = sf.openSession();
        s.beginTransaction();
        System.out.println("log1");
        Product p = (Product)s.get(Product.class, 5);
        System.out.println("log2");
        Product p2 = (Product)s.load(Product.class, 5);
        System.out.println("log3");
         
        Product p3 = (Product)s.get(Product.class, 500);
        System.out.println("p3="+p3);
         
        Product p4 = (Product)s.load(Product.class, 500);
        System.out.println("p3="+p4);
         
        s.getTransaction().commit();
        s.close();
        sf.close();
    }
}

九、两种session方式

Hibernate有两种方式获得session,分别是: 
openSession和getCurrentSession 
他们的区别在于 
1. 获取的是否是同一个session对象
openSession每次都会得到一个新的Session对象 
getCurrentSession在同一个线程中,每次都是获取相同的Session对象,但是在不同的线程中获取的是不同的Session对象 
2. 事务提交的必要性 
openSession只有在增加,删除,修改的时候需要事务,查询时不需要的 
getCurrentSession是所有操作都必须放在事务中进行,并且提交事务后,session就自动关闭,不能够再进行关闭 
OpenSession每次都会得到一个新的Session对象
package com.how2java.test;
  
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
  /*
  OpenSession每次都会得到一个新的Session对象,所以System.out.println(s1==s2);会打印false
  */
public class TestHibernate {
    public static void main(String[] args) {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
  
        Session s1 = sf.openSession();
        Session s2 = sf.openSession();
         
        System.out.println(s1==s2);
         
        s1.close();
        s2.close();
        sf.close();
    }
}

在这里插入图片描述

相同线程的getCurrentSession
package com.how2java.test;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 /*
 如果是同一个线程(本例是在主线程里),每次获取的都是相同的Session
 */
public class TestHibernate {
    public static void main(String[] args) {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
 
        Session s1 = sf.getCurrentSession();
        Session s2 = sf.getCurrentSession();
 
        System.out.println(s1 == s2);
 
        s1.close();
//      s2.close();
        sf.close();
    }
}

在这里插入图片描述

不同线程的getCurrentSession
package com.how2java.test;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
 /*
 如果是不同线程,每次获取的都是不同的Session
 */
public class TestHibernate {
    static Session s1;
    static Session s2;
 
    public static void main(String[] args) throws InterruptedException {
 
        final SessionFactory sf = new Configuration().configure().buildSessionFactory();
 
        Thread t1 = new Thread() {
            public void run() {
                s1 = sf.getCurrentSession();
            }
        };
        t1.start();
 
        Thread t2 = new Thread() {
            public void run() {
                s2 = sf.getCurrentSession();
            }
        };
        t2.start();
        t1.join();
        t2.join();
 
        System.out.println(s1 == s2);
    }
 
}

在这里插入图片描述

openSession查询时候不需要事务
package com.how2java.test;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
import com.how2java.pojo.Product;
 /*
 如果是做增加,修改,删除是必须放在事务里进行的。 但是如果是查询或者get,那么对于openSession而言就不需要放在事务中进行
 */
public class TestHibernate {
    static Session s1;
    static Session s2;
 
    public static void main(String[] args) throws InterruptedException {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
 
        Session s1 = sf.openSession();
 
        s1.get(Product.class, 5);
 
        s1.close();
 
        sf.close();
    }
}
getCurrentSession所有操作都必须放在事务中
package com.how2java.test;
 
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) throws InterruptedException {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
 
        Session s1 = sf.getCurrentSession();
 
        s1.get(Product.class, 5);
 
        s1.close();
 
        sf.close();
    }
 
}

对于getCurrentSession而言所有操作都必须放在事务中,甚至于查询和get都需要事务。
16行的get没有放在事务中,就会导致异常产生
在这里插入图片描述

getCurrentSession在提交事务后,session自动关闭
package com.how2java.test;
 
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) throws InterruptedException {
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
 
        Session s1 = sf.getCurrentSession();
 
        s1.beginTransaction();
 
        s1.get(Product.class, 5);
 
        s1.getTransaction().commit();
         
        s1.close();
 
        sf.close();
    }
 
}

22行,在事务关闭后,试图关闭session,就会报session已经关闭的异常
在这里插入图片描述

十、N+1

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

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

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

如果id在缓存中,就从缓存中获取product对象了,否则再从数据库中获取
Hibernate使用Iterator实现N 1
package com.how2java.test;
  
import java.util.Iterator;
 
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
import com.how2java.pojo.Product;
  /*
  首先通过Query的iterator把所有满足条件的Product的id查出来

然后再通过it.next()查询每一个对象
如果这个对象在缓存中,就直接从缓存中取了
否则就从数据库中获取

N+1中的1,就是指只返回id的SQL语句,N指的是如果在缓存中找不到对应的数据,就到数据库中去查
  */
public class TestHibernate {
    public static void main(String[] args) {
        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+"%");
        
        Iterator<Product> it= q.iterate();
        while(it.hasNext()){
            Product p =it.next();
            System.out.println(p.getName());
        }
        
        s.getTransaction().commit();
        s.close();
        sf.close();
    }
  
}

image

十一、查询总数

返回满足条件的总数

首先还是准备一个有统计函数的语句

select count(*) from ....

根据这条SQL语句创建一个Query对象,调用Query对象的uniqueResult()方法,返回一个long型的数据,即查询总数。

package com.how2java.test;
  
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
  
public class TestHibernate {
    public static void main(String[] args) {
        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();
    }
  
}

十二、乐观锁

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

首先看不使用乐观锁的情况

故意创造一个场景来制造脏数据。

  1. 通过session1得到id=1的对象 product1
  2. 在product1原来价格的基础上增加1000
  3. 更新product1之前,通过session2得到id=1的对象product2
  4. 在product2原来价格的基础上增加1000
  5. 更新product1
  6. 更新product2

最后结果是product的价格只增加了1000,而不是2000

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

image

修改配置文件 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>
修改 Product.java
package com.how2java.pojo;
  
import java.util.Set;
  
public class Product {
    int id;
    String name;
    float price;
    Category category;
    int version;
    public int getVersion() {
        return version;
    }
    public void setVersion(int version) {
        this.version = version;
    }
    Set<User> users;
  
    public Set<User> getUsers() {
        return users;
    }
    public void setUsers(Set<User> users) {
        this.users = users;
    }
    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;
    }
      
}
重新运行一样的代码
package com.how2java.test;
 
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 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();
    }
 
}

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

这样就保证了数据的一致性。
在这里插入图片描述

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

在这里插入图片描述

十三、C3P0连接池

建立数据库连接时比较消耗时间的,所以通常都会采用数据库连接池的技术来建立多条数据库连接,并且在将来持续使用,从而节约掉建立数据库连接的时间 
hibernate本身是提供了数据库连接池的,但是hibernate官网也不推荐使用他自带的数据库连接池。 
一般都会使用第三方的数据库连接池 
C3P0是免费的第三方的数据库连接池,并且有不错的表现 
右边可以下载c3p0的jar包 

注:当运行次数不大的时候,从运行效果上来看,是看不出区别的。 只有在高并发量的情况下,才会体会出来。本知识主要是提供这个相关配置办法,以供以后有需要的时候,查询与修改方便。 
1、jar包
2、进行c3p0连接池的配置

加上相应的配置即可,代码无需做任何改动

<?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=GBK</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>
          
          <!--配置连接池-->
        <property name="hibernate.connection.provider_class"> 
            org.hibernate.connection.C3P0ConnectionProvider 
        </property> 
        <property name="hibernate.c3p0.max_size">20</property> 
        <property name="hibernate.c3p0.min_size">5</property> 
        <property name="hibernate.c3p0.timeout">50000</property> 
        <property name="hibernate.c3p0.max_statements">100</property> 
        <property name="hibernate.c3p0.idle_test_period">3000</property> 
        <!-- 当连接池耗尽并接到获得连接的请求,则新增加连接的数量 -->
        <property name="hibernate.c3p0.acquire_increment">2</property> 
        <!-- 是否验证,检查连接 -->
        <property name="hibernate.c3p0.validate">false</property>           
          
        <mapping resource="com/how2java/pojo/Product.hbm.xml" />
        <mapping resource="com/how2java/pojo/Category.hbm.xml" />
        <mapping resource="com/how2java/pojo/User.hbm.xml" />
          
    </session-factory>
  
</hibernate-configuration>
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="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>

注解

一、示例

Hibernate的注解是什么?

简单的说,本来放在hbm.xml文件里的映射信息,现在不用配置文件做了,改由注解来完成

Product.hbm.xml

这是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>
        <property name="name" />
        <property name="price" />
    </class>
      
</hibernate-mapping>
注解方式

使用注解之后的Product.java

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 = "product_")
public class Product {
    int id;
    String name;
    float price;
     
    @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;
    }
      
}
hibernate.cfg.xml 配置文件改动

为了能够让hibernate支持这种注解的方式,还需要做一个改动:
把hibernate.cfg.xml配置文件中的

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

注释掉,采用:

<mapping class="com.how2java.pojo.Product" />

这种写法,其他所有代码都不用改变,这样就可以一样实现hibernate的CRUD操作

<?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" />
 
    </session-factory>
  
</hibernate-configuration>

二、类与属性

1、hibernate 注解分类

hibernate里常用注解包括,类注解,属性注解,关系注解,其他的注解

2、类注解

在注解示例-注解方式的Product中,Product类声明前面有两个注解:
@Entity 和 @Table(name = "product_")
@Entity 表示这是一个实体类,用于映射表
@Table(name = "product_") 表示这是一个类,映射到的表名:product_

示例:

@Entity
@Table(name = "product_")
public class Product {
    int id;
    String name;
    float price;
}

3、属性注解

然后是属性注解,属性注解是配置在属性对应的getter方法上的

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) 
@Column(name = "id")   
public int getId() {
    return id;
}

@Id 表示这是主键
@GeneratedValue(strategy = GenerationType.IDENTITY) 表示自增长方式使用mysql自带的
@Column(name = “id”) 表示映射到字段id

@Column(name = "name")
public String getName() {
    return name;
}

表示name属性映射表的name字段

@Column(name = "price")
public float getPrice() {
    return price;
}

表示price属性映射表的price字段

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 = "product_")
public class Product {
    int id;
    String name;
    float price;
     
    @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;
    }
      
}

三、关系

1、多对一注解

把多对一改成用注解来实现

  1. 把Category的id和name字段改为支持注解

注: 分类的getName上并没有加上@Column(name=“name”),也可以达到映射的效果。 因为getName方法默认会被认为是字段映射。 除非加上了@Transient 才表示不进行映射
2. 把Product的getCategory进行多对一映射

@ManyToOne
    @JoinColumn(name="cid") 
    public Category getCategory() {
        return category;
    }

@ManyToOne 表示多对一关系
@JoinColumn(name=“cid”) 表示关系字段是cid
对比xml中的映射方式:

<many-to-one name="category" class="Category" column="cid" />
  1. 为hibernate.cfg.xml 添加Category的映射
<mapping class="com.how2java.pojo.Category" />

Category.java

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

Produce.java

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>

二、一对多注解

在上一步的基础上做如下改动

  1. 为Category再加product集合,并提供getter和setter
Set<Product> products;
    public Set<Product> getProducts() {
        return products;
    }
    public void setProducts(Set<Product> products) {
        this.products = products;
    }
  1. 给getProducts方法加上一对多注解
@OneToMany(fetch=FetchType.EAGER)
    @JoinColumn(name="cid") 
    public Set<Product> getProducts() {
        return products;
    }

@OneToMany 表示一对多,fetch=FetchType.EAGER 表示不进行延迟加载(FetchType.LAZY表示要进行延迟加载)
@JoinColumn(name=“cid”) 表示映射字段
对比xml中的映射方式:

    <set name="products" lazy="false">
        <key column="cid" not-null="false" />
        <one-to-many class="Product" />
    </set>
  1. 修改TestHibernate为

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

Category.java

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

TestHibernate.java

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

3、多对多注解

  1. 在基于XML配置的多对多知识点的基础上进行多对多注解的修改

2.像上两步那样,为Product,User,Category 加上类和属性注解

  1. 加上多对一注解ManyToOne

  2. 加上一对多注解OneToMany

  3. ManyToMany
    为Product的getUsers加上

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

对比Product.hbm.xml中的配置:

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

为User的getProducts加上

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

对比User.hbm.xml中的配置

    <set name="products" table="user_product" lazy="false">
        <key column="uid" />
        <many-to-many column="pid" class="Product" />
    </set>   
  1. hibernate.cfg.xml

     <mapping class="com.how2java.pojo.Product" />
     <mapping class="com.how2java.pojo.Category" />
     <mapping class="com.how2java.pojo.User" />
    

User.java

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.java

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

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 class="com.how2java.pojo.Product" />
        <mapping class="com.how2java.pojo.Category" />
        <mapping class="com.how2java.pojo.User" />
 
    </session-factory>
  
</hibernate-configuration>

TestHibernate.java

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

四、注解手册

1、类相关注解

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

2、属性相关注解

@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.

3、关系相关注解

@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.

猜你喜欢

转载自blog.csdn.net/qq_38846837/article/details/86549153
今日推荐