一、概括
是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的JaveEE架构中取代CMP,完成数据持久化的重任。
二、入门
1、poem.xml 引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.chen</groupId>
<artifactId>hibernate</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.10.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
</dependencies>
</project>
2、配置文件:hibernate.cfg.xml
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Hibernate配置文件的DTD信息 -->
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- hibernate- configuration是连接配置文件的根元素 -->
<hibernate-configuration>
<session-factory>
<!--数据源配置-->
<property name="connection.username">root</property>
<property name="connection.password">chenguangwei</property>
<property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8</property>
<!-- C3P0数据连接池 -->
<property name="hibernate.c3p0.acquire_increment">10</property>
<property name="hibernate.c3p0.idle_test_period">10000</property>
<property name="hibernate.c3p0.timeout">5000</property>
<property name="hibernate.c3p0.max_size">30</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_statements">10</property>
<!-- 数据库方言 -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 打印SQL -->
<property name="show_sql">true</property>
<!-- 格式化SQL语句 -->
<property name="format_sql">true</property>
<!-- 是否自动生成生成数据表 -->
<property name="hibernate.hbm2ddl.auto">create</property>
</session-factory>
</hibernate-configuration>
3、创建实体类
@Data
public class Customer {
private Integer id;
private Set<Orders> orders;
}
@Data
public class Orders {
private Integer id;
//订单名
private String name;
private Customer customer;
}
4、创建实体关系映射文件
实体:
@Data
public class People {
private Integer id;
private String name;
private Double money;
}
映射文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 建立类与表的映射 -->
<class name="com.chen.entity.People" table="people">
<!-- 建立类中的属性与表中的主键对应 -->
<id name="id" column="id" type="java.lang.Integer">
<generator class="identity"/>
</id>
<!-- 建立类中的普通的属性和表的字段的对应 -->
<property name="name" type="java.lang.String">
<column name="name"></column>
</property>
<property name="money" type="java.lang.Double" column="money" />
</class>
</hibernate-mapping>
5、实体关系映射文件注册到Hibernate的配置文件中
<!-- 注册实体关系映射文件 -->
<mapping resource="com/chen/entity/People.hbm.xml"></mapping>
执行测试方法往数据库表中插入数据:
public class Test {
public static void main(String[] args) {
//创建Configuratiin
Configuration configuration = new Configuration().configure();
//获取SessionFactory
SessionFactory sessionFactory = configuration.buildSessionFactory();
//获取Session
Session session = sessionFactory.openSession();
People people = new People();
people.setName("小Y");
people.setMoney(100.05);
session.save(people);
session.beginTransaction().commit();
}
}
报错:Exception in thread "main" org.hibernate.boot.MappingNotFoundException: Mapping (RESOURCE) not found : com/chen/entity/People.hbm.xml : origin(com/chen/entity/People.hbm.xml) at org.hibernate.boot.spi.XmlMappingBinderAccess.bind(XmlMappingBinderAccess.java:56) at org.hibernate.boot.MetadataSources.addResource(MetadataSources.java:294) at org.hibernate.boot.cfgxml.spi.MappingReference.apply(MappingReference.java:70) at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:468) at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:84) at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:689) at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:724) at com.chen.test.Test.main(Test.java:13)
无法读取到xxx.xml资源文件:需要在poem.xml中配置resource:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
又报错:java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'type=MyISAM'
原因是hibernate配置文件中开启自动生成数据表的原因,索性直接把它关了,手动去数据库建一个people表:
运行成功:
查询数据库,数据存在:
6、Hibernate级联操作
(1)一对多关系
客户和订单:每个客户可以购买多个产品,生成多个订单,但是每一个订单只属于一个客户,所以:客户->订单=1->多
A、数据库是通过主外键实现:1为主表,多为从表(添加外键)。
例:
a.创建表
CREATE TABLE customer(
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE orders(
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
cid INT(11),
PRIMARY KEY(id),
CONSTRAINT `FK_CID_CUSTOMER_ID` FOREIGN KEY (cid) REFERENCES customer(id)
);
B、面向对象中通过以下方式实现一对多关系。
例:
a.创建实体类
@Data
public class Customer {
private Integer id;
private String name;
//一个Customer对应多个orders
private Set<Orders> orders;
}
@Data
public class Orders {
private Integer id;
private String name;
//一个Order对应一个Customer
private Customer customer;
}
b.创建映射文件
Customer.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 建立类与表的映射 -->
<class name="com.chen.entity.Customer" table="customer">
<!-- 建立类中的属性与表中的主键对应 -->
<id name="id" column="id" type="java.lang.Integer">
<generator class="identity"/>
</id>
<!--建立类中的普通属性与表中属性对应-->
<property name="name" type="java.lang.String">
<column name="name"></column>
</property>
<!-- set标签用来配置实体类中的集合属性orders:table->表名 key->外键 one-to-many:集合泛型的实体类对象 -->
<set name="orders" table="orders">
<key column="cid"></key>
<one-to-many class="com.chen.entity.Orders"></one-to-many>
</set>
</class>
</hibernate-mapping>
Order.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 建立类与表的映射 -->
<class name="com.chen.entity.Orders" table="orders">
<!-- 建立类中的属性与表中的主键对应 -->
<id name="id" column="id" type="java.lang.Integer">
<generator class="identity"/>
</id>
<!-- 建立类中的普通属性与表中属性对应 -->
<property name="name" type="java.lang.String">
<column name="name"></column>
</property>
<!-- 建立类中的类属性与表对应:name->属性名 class->属性对应的类 column->关联外键 -->
<many-to-one name="customer" class="com.chen.entity.Customer" column="cid"></many-to-one>
</class>
</hibernate-mapping>
c.在Hibernate配置文件中注册映射文件
<mapping resource="com/chen/entity/Customer.hbm.xml"></mapping>
<mapping resource="com/chen/entity/Oreders.hbm.xml"></mapping>
d.通过Hibernate API去调用:
public class Test1 {
public static void main(String[] args) {
//创建Configuratiin
Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
//获取SessionFactory
SessionFactory sessionFactory = configuration.buildSessionFactory();
//获取Session
Session session = sessionFactory.openSession();
//创建Customer对象
Customer customer = new Customer();
customer.setName("孟佳");
//创建Oreders
Orders orders = new Orders();
orders.setName("订单H");
//建立关系
orders.setCustomer(customer);
//保存
session.save(customer);
session.save(orders);
//提交事务
session.beginTransaction().commit();
}
}
e.结果
控制台打印sql语句:
查询数据库:
(2)多对多关系
学生选课:一门课程可以被多个学生选择,一个学生可以选择多门课,所以:学生->课程=多->多
A、数据库是通过两个一对多关系来维护,学生和课程都是主表,额外增加一张中间表作为从表。
例:
创建数据库表
CREATE TABLE account(
id INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE course(
id INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE account_course(
id INT(11) NOT NULL AUTO_INCREMENT,
a_id INT(11),
c_id INT(11),
CONSTRAINT `FK_AID_ACCOUNT_ID` FOREIGN KEY (a_id) REFERENCES account(id),
CONSTRAINT `FK_CID_COURSE_ID` FOREIGN KEY (c_id) REFERENCES course(id),
PRIMARY KEY(id)
);
B、面向对象中通过以下方式实现多对多关系。
例:
a.创建实体类
@Data
public class Account {
private Integer id;
private String name;
//一个学生可以选择多门课
private Set<Course> courses;
}
@Data
public class Course {
private Integer id;
private String name;
//一个课程可以被多人选择
private Set<Account> accounts;
}
b.创建映射文件
Account.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 建立类与表的映射 -->
<class name="com.chen.entity.Account" table="account">
<!-- 建立类中的属性与表中的主键对应 -->
<id name="id" column="id" type="java.lang.Integer">
<generator class="identity"/>
</id>
<!--建立类中的普通属性与表中属性对应-->
<property name="name" type="java.lang.String">
<column name="name"></column>
</property>
<!-- set标签用来配置实体类中的集合属性courses:table->中间表表名 key->当前类在中间表中的外键 -->
<!-- many-to-many:多对多关系:class->指定courses属性对应的实体类 column->该实体类在中间表中的外键 -->
<set name="courses" table="account_course">
<key column="a_id"></key>
<many-to-many class="com.chen.entity.Course" column="c_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
Course.hbm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 建立类与表的映射 -->
<class name="com.chen.entity.Course" table="course">
<!-- 建立类中的属性与表中的主键对应 -->
<id name="id" column="id" type="java.lang.Integer">
<generator class="identity"/>
</id>
<!--建立类中的普通属性与表中属性对应-->
<property name="name" type="java.lang.String">
<column name="name"></column>
</property>
<!-- set标签用来配置实体类中的集合属性courses:table->中间表表名 key->当前类在中间表中的外键 -->
<!-- many-to-many:多对多关系:class->指定accounts属性对应的实体类 column->该实体类在中间表中的外键 -->
<set name="accounts" table="account_course">
<key column="c_id"></key>
<many-to-many class="com.chen.entity.Account" column="a_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
c.在Hibernate配置文件中注册映射文件
<mapping resource="com/chen/entity/Account.hbm.xml"></mapping>
<mapping resource="com/chen/entity/Customer.hbm.xml"></mapping>
d.通过Hibernate API去调用:
public class Test2 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
//创建course对象
Course course = new Course();
course.setName("大学物理");
//创建account对象
Account account = new Account();
account.setName("胡帅");
//建立关系
/*Set<Account> accounts = new HashSet<Account>();
accounts.add(account);
course.setAccounts(accounts);*/
Set<Course> courses = new HashSet<Course>();
courses.add(course);
account.setCourses(courses);
//保存
session.save(course);
session.save(account);
//提交事务
session.beginTransaction().commit();
session.close();
}
}
e.结果
可以从控制台打印SQL语句看出Hibernate把我们建立的关系通过sql语句的形式插入到中间表里(很智能!)。
补:
Java和数据库对于两种关系的体现完全是两种不同的方式,Hibernate框架的作用就是将这两种方式进行转换和映射
。
7、Hibernate延迟加载:也称为惰性加载或懒加载
1)提高程序的运行效率
众所周知:Java程序与数据库交互的频次越低,程序的运行效率就越高。为了尽量减少两者之间交互次数,Hibernate延迟加载应运而生。
2)诞生背景
客户和订单:当我们查询客户对象时,因为有级联设置,所以会将对应的订单信息一并查出来,这样就需要发送两条sql语句(两次交互),分别查询客户信息和订单信息。
延迟加载的思路
:当我们查询客户的时候,如果不涉及订单数据,只发送一条sql语句(一次交互)查询客户信息;如果涉及订单数据,才会发送两条sql(两次交互)。
延迟加载可以看作是一种优化机制,根据需求不同,自动选择要执行的sql语句条数(交互次数)。
3)应用场景
(1)一对多关系(沿用之前Customer和ordes的例子)
A、查询Customer, 对Oredes进行延迟加载设置。在customer.hbm.xml进行设置,延时加载默认是开启的(lazy="true"
)。
<set name="orders" table="orders" lazy="true">
<key column="cid"></key>
<one-to-many class="com.chen.entity.Orders"></one-to-many>
</set>
a、只查询Customer信息,不涉及Oreders(需要注意的是当调用toString()方法的时候,需要对原实体类@Data注解进行切换,否则会造成内存溢出错误,因为toString()方法中设计Oredes数据,程序会发送sql去查询orders数据,而orders对象打印有需要customer数据,两者之间造成循环调用
):
@Getter
@Setter
public class Customer {
private Integer id;
private String name;
private Set<Orders> orders;
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
@Setter
@Getter
public class Orders {
private Integer id;
private String name;
private Customer customer;
@Override
public String toString() {
return "Orders{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
public class Test3 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
Customer customer = session.get(Customer.class,10);
System.out.println(customer.toString());
session.close();
}
}
控制台只打印一条sql语句:
b、当查询Customer设计orders属性时:
控制台打印了两条sql语句:
这就是延时加载,Hibernate会根据需求来选择sql语句的条数。
B、查询Customer,不对Oredes进行延迟加载设置。在customer.hbm.xml进行设置,将延时加载关闭(lazy="flase"
)。
<set name="orders" table="orders" lazy="false">
<key column="cid"></key>
<one-to-many class="com.chen.entity.Orders"></one-to-many>
</set>
a、查询Customer不涉及oredes级联属性:
public class Test3 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
Customer customer = session.get(Customer.class,10);
System.out.println(customer);
session.close();
}
}
控制台打印了两条sql语句,病没有根据需求来选择与数据库的交互次数:
C、lazy="extra"
,extra是比true更加懒惰(智能)的一种加载方式:
a、查询Customer对象,打印该对象对应的orders集合的长度(设置lazy="true"
)
<!--set标签lazy默认为true-->
<set name="orders" table="orders" lazy="true">
<key column="cid"></key>
<one-to-many class="com.chen.entity.Orders"></one-to-many>
</set>
执行:
public class Test3 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
Customer customer = session.get(Customer.class,10);
System.out.println(customer.getOrders().size());
session.close();
}
}
控制台打印:
其原理:从orders表中查询出符合条件的数据分别封装成Orders对象,然后把这些对象加到集合里面,然后再调用Customer的setter方法把这个集合赋值给orders属性,然后在调用集合的size()方法打印结果。因为此处只是单纯的想知道长度而无需知道orders具体属性是什么,所以有点浪费资源。
b、相同的目的(设置lazy="extra"
)
<set name="orders" table="orders" lazy="extra">
<key column="cid"></key>
<one-to-many class="com.chen.entity.Orders"></one-to-many>
</set>
控制台打印:
可以看出此时查询数据直接用count(id)查询符合条件的条数,更加高效。
C、查询Orders,对Customer选择进行延迟加载与否。在Orders.hbm.xml进行配置lazy的值:
< many-to-one>的lazy值:
默认情况下为false:表示关闭延时加载
no-proxy:当调用方法需要访问customer的成员变量时,发送SQL语句查询Customer,否则不查询。
proxy:无论调用方法是否需要访问customer的成员变量,都会发送SQL语句查询Customer。
(2)多对多关系(沿用之前Account和Course的例子,跟上面相同为了避免循环调用导致内存溢出,将@Data注解换为@Setter和@Getter,并重写toString()发法)
A、查询Course,加载对应的Account,在Course.hbm.xml中配置。默认延迟加载为开启的:
<set name="accounts" table="account_course" lazy="true">
<key column="c_id"></key>
<many-to-many class="com.chen.entity.Account" column="a_id"></many-to-many>
</set>
a、根据id查询Course不涉及Account:
public class Test4 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
Course course = session.get(Course.class,2);
System.out.println(course);
session.close();
}
}
打印结果:
b、根据id查询Course涉及Account:
public class Test4 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
Course course = session.get(Course.class,2);
System.out.println(course.getAccounts());
session.close();
}
}
控制台打印:
可已看出发送了两条SQL语句,跟前面一对多中标签是否开启懒加载原理相同,开启之后就能够根据需求来判断是否能够减少数据交互次数。同理设置lazy="extra"
Hibrenate会更加高效,包括通过Account查询Course,原理都相同不再赘述。
8、Hibernate配置文件详解
(1)hibernate.cfg.xml
:配置Hibernate的全局环境
A、数据库的基本信息。
B、集成C3p0,设置数据库连接池信息。
C、Hibernate基本信息
<!-- 数据库方言 -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 打印SQL -->
<property name="show_sql">true</property>
<!-- 格式化SQL语句 -->
<property name="format_sql">true</property>
<!--是否自动生成生成数据表
update:动态创建表,如果表存在直接使用;否则则创建
create:无论是否存在都创建,存在则先删除
create-drop:初始化创建表,程序结束时删除表
validate:校验实体关系映射文件和数据表是否对应,不对应直接报错
-->
<property name="hibernate.hbm2ddl.auto"></property>
D、注册映射文件
<mapping resource="com/chen/entity/People.hbm.xml"></mapping>
(2)XXXX.hbm.xml
:实体关系映射文件:XXXX表示实体类名
例如:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 建立类与表的映射 -->
<class name="com.chen.entity.Account" table="account">
<!-- 建立类中的属性与表中的主键对应 -->
<id name="id" column="id" type="java.lang.Integer">
<generator class="identity"/>
</id>
<!--建立类中的普通属性与表中属性对应-->
<property name="name" type="java.lang.String">
<column name="name"></column>
</property>
<!-- set标签用来配置实体类中的集合属性courses:table->中间表表名 key->当前类在中间表中的外键 -->
<!-- many-to-many:多对多关系:class->指定courses属性对应的实体类 column->该实体类在中间表中的外键 -->
<set name="courses" table="account_course">
<key column="a_id"></key>
<many-to-many class="com.chen.entity.Course" column="c_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
A、 < hibernate-mapping>的属性:
auto-import
:指定我们是否可以在查询语句中使用非全限定类名,默认为true。如果项目中有两个同名的持久化类,最好在这两个类对应的映射文件中设置为false。
catalog
:数据库catalog的名称。
default-access
:Hibernate用来访问属性的策略。
default-lazy
:指定未明确注明lazy属性的Java属性和集合类,Hibernate会采用什么样的加载风格,默认为true。
default-cascade
:默认的级联关系,默认为none。
schema
:数据库schema的名称。
·package·:给class节点对应的实体类统一设置包名。此处设置了包名,class节点的就可以name属性就可以省略包名。
B、 < hibernate-mapping>子标签< class>的属性:
name
:实体类名。
table
:数据表名。
schema
:数据库schema的名称,会覆盖hibernate-mapping的schema。
catalog
:数据库catalog的名称,会覆盖hibernate-mapping的catalog。
proxy
:指定一个接口,在延时加载时作为代理使用。
dynamic-update
:动态更新。
dynamic-insert
:动态添加(默认为false)。
where
:查询时给SQL添加where条件。
例:什么叫做动态添加(更新)?
public class Test5 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
People people = new People();
people.setName("唐雅文");
session.save(people);
session.beginTransaction().commit();
session.close();
}
}
我们只是为People的name属性设了值,money属性并未设置,执行main、方法控制台打印:
查询数据库:
发现程序执行插入的时候为money加入了个null,而这个操作数据库同样可以完成,所以无需java程序为字段插入一个空置。引入动态添加:
<class name="com.chen.entity.People" table="people" dynamic-insert="true">
再次执行插入:
此次只是插入了name字段属性,查询数据库,效果与之前一样,但是动态加载可以忽略为空字段的加入操作:
C、< class>子标签< id>的属性:
name
:实体类的属性名。
type
:实体类属性数据类型。
- 此处可以设置两种类型的数据:Java数据类型huozhe或者Hibernate映射类型。
- 实体类的属性数据类型必须与数据表对应的字段数据类型一致:int对int、String对varchar等
- 如何进行映射?
java数据类型——>Hibernate映射类型——>SQL数据类型
column
:数据表的主键字段名。
generator
:主键生成策略。
- hilo算法
- increment:Hibernate自增
- identity:数据库自增
- native:本地策略,根据底层数据库自动选择主键的生成策略
- uuid.hex算法
- select算法
C、< class>子标签< property>的属性:
name
:实体类的属性名。
column
:数据表的字段名。
type
:数据类型。
update
:该字段是否可以修改(默认为true)。
insert
·:该字段是否可以添加(默认为true)。
lazy
:延时加载策略
D、 < set>标签属性inverse:用来解决双向维护
Customer和Orders是一对多关系,一个Customer对应多个Oreders,实体类中用一个set集合来表示对应的Oreders。
Customer.hbm.xml中使用set标签来配置映射关系。
/**
* 双向维护问题
* 例如Custoemr和Orders:
* Customer.hbm.xml中维护一对多关系
* Orders.hbm.xml中维护多对一关系
*/
public class Test6 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
//创建Customer对象
Customer customer = new Customer();
customer.setName("柯凡勾");
//创建Orders对象
Orders oreders = new Orders();
oreders.setName("订单I");
//创建第二个Orders对象
Orders orders1 = new Orders();
orders1.setName("订单J");
//Orders维护关系
oreders.setCustomer(customer);
orders1.setCustomer(customer);
//Customer维护关系
Set<Orders> ordersSet = new HashSet<Orders>();
ordersSet.add(oreders);
ordersSet.add(orders1);
customer.setOrders(ordersSet);
//保存
session.save(oreders);
session.save(orders1);
session.save(customer);
//提交
session.beginTransaction().commit();
session.close();
}
}
执行控制台打印:
可以看出当开始根据关系往表中插入数据之后,又进行了两次次更新操作。因为Customer和Orders都在维护这个一对多关系,所以会重复设置主外键约束关系。
如何避免这种情况呢?
- 在Java代码中去掉一方维护关系代码
- 通过改变hbm.xml配置文件解决(设置
inverse="true"
)
<set name="orders" table="orders" lazy="extra" inverse="true">
<key column="cid"></key>
<one-to-many class="com.chen.entity.Orders"></one-to-many>
</set>
控制台打印结果,只进行了三次插入,没有后续更新操作,所以双向维护问题得到了解决:
注:
inverse属性是用来设置是否将维护权交给对方,默认为false:不交出维护权,双方都在维护。
E、 < set>标签属性cascade:用来设置级联操作
public class Test7 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
Customer customer = session.get(Customer.class,19);
session.delete(customer);
session.beginTransaction().commit();
session.close();
}
}
执行控制台报错:
当通过以上方式删除一个Customer时,因为Orders通过外键关联到Customer表,直接从Customer删除数据需遵循先删子表再删主表原则
。
解决方式1:先删子表(Oreders)数据,再删主表数据
public class Test7 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
Customer customer = session.get(Customer.class,19);
Iterator<Orders> ordersIterator = customer.getOrders().iterator();
while(ordersIterator.hasNext()){
session.delete(ordersIterator.next());
}
session.delete(customer);
session.beginTransaction().commit();
session.close();
}
}
解决方式2:在实体映射文件中设置cascade值完成级联删除
<set name="orders" table="orders" lazy="extra" inverse="true" cascade="delete">
<key column="cid"></key>
<one-to-many class="com.chen.entity.Orders"></one-to-many>
</set>
9、Hibernate-HQL
- Hibernate Query Language,是Hibernate 框架提供的一种查询机制。他和SQL类似,不同的是HQL是面向对象的查询语句,让开发者能够以面向对象的思想来编写查询语句,对Java编程是一种友好的方式。
- HQL不能直接参与数据库的交互,属于中间层语言:Java——》HQL——》Hibernate——》SQL——》DB
- HQL只能完成查询、修改和删除,不支持新增。
(1)普通查询
查询表中所有数据,自动完成对象的封装,返回List集合。
HQL进行查询,from关键字后面不能写表名,必须写表对应的实体类名。
public class Test8 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
String hql = "from People";
Query query = session.createQuery(hql);
List<People> list = query.list();
for(People people:list){
System.out.println(people);
}
session.beginTransaction().commit();
session.close();
}
}
(2)分页查询
- HQL分页查询可以通过query的方法来完成。
A、setFirstResult():设置起始下标
B、setMaxResults():设置截取长度
从下标为1开始查询,总共查询3条:
public class Test8 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
String hql = "from People";
Query query = session.createQuery(hql);
query.setFirstResult(1);
query.setMaxResults(3);
List<People> list = query.list();
for(People people:list){
System.out.println(people);
}
session.beginTransaction().commit();
session.close();
}
}
数据库表数据:
控制台打印分页查询结果:
(3)where条件查询
- HQL直接追加where关键字作为查询条件,与SQL没有区别。
public class Test8 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
String hql = "from People where id =8";
Query query = session.createQuery(hql);
People people = (People) query.list().get(0);
System.out.println(people);
session.beginTransaction().commit();
session.close();
}
}
补:
People people = (People) query.list().get(0);
返回一个集合,此时集合只有一个对象,通过下标0取出该对象。此方法当查询的id不存在时会报异常。
如果使用People people = (People)query.uniqueResult();
当查询条件不存在时不会报异常会返回一个null。
(4)模糊查询
- 查询名字中带有‘a‘的所有记录:
public class Test8 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
String hql = "from People where name like '%a%'";
Query query = session.createQuery(hql);
List<People> list = query.list();
for(People people:list){
System.out.println(people);
}
session.beginTransaction().commit();
session.close();
}
}
查询结果:
(5)order by排序
- 按照‘money’列排序(默认为升序):
public class Test8 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
String hql = "from People order by money";
Query query = session.createQuery(hql);
List<People> list = query.list();
for(People people:list){
System.out.println(people);
}
session.beginTransaction().commit();
session.close();
}
}
控制台查询结果:
补:
升序:String hql = "from People order by money asc";
降序:String hql = "from People order by money desc";
(6)查询实体对象的属性
- 只查询People的那么属性:
public class Test8 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
String hql = "select name from People where id=9";
Query query = session.createQuery(hql);
String name = (String) query.uniqueResult();
System.out.println(name);
session.beginTransaction().commit();
session.close();
}
}
控制台查询结果:
(7)占位符
- 占位符使用
public class Test8 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
String hql = "from People where name = :name";
Query query = session.createQuery(hql);
query.setString("name","李白");
List<People> list = query.list();
for(People people:list){
System.out.println(people);
}
session.beginTransaction().commit();
session.close();
}
}
控制台打印结果:
(8)级联查询:以Orders和Customer为例
- 查询用户名为‘小阳’的用户所有订单
public class Test8 {
public static void main(String[] args) {
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
String hql = "from Customer where name = :name";
Query query = session.createQuery(hql);
query.setString("name","小阳");
Customer customer = (Customer) query.uniqueResult();
String hql1 = "from Orders where customer = :customer";
Query query1 =session.createQuery(hql1);
query1.setEntity("customer",customer);
List<Orders> list = query1.list();
for(Orders orders:list){
System.out.println(orders);
}
session.beginTransaction().commit();
session.close();
}
}
控制台打印结果: