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>
重新运行一样的代码
做同样的业务就会抛出异常,提示该行已经被其他事务删除或者修改过了,本次修改无法生效。
原理
- 假设数据库中产品的价格是10000,version是10
- session1,session2分别获取了该对象
- 都修改了对象的价格
- session1试图保存到数据库,检测version依旧=10,成功保存,并把version修改为11
- 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" />
<!–新增Category映射–>
<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();
}
}