标签:特殊 inf 需求 帮助 provider 名称 映射文件 同名 协助
首发日期:2018-07-31
题外话:什么是ORM(对象关系映射)?
ORM 将数据库中的表与面向对象语言中的类建立了一种对应关系,【ORM可以说是参照映射来处理数据的模型,比如说:需要创建一个表,可以定义一个类,而这个类存在与表相映射的属性,那么可以通过操作这个类来创建一个表】
下面的运行环境基于的是Hibernate5.3.0
1.在官网下载Hibernate5
2.解压下载的压缩包
3.在解压出来的文件夹中,lib存放着Hibernate运行所需要的jar包。
4.因为这里仅仅做最基础的运行,所以仅仅将required中的11个jar包导入到工程中。
如果你还需要其他功能,按照前面图中标注不同功能的文件夹来选择添加。比如想要使用C3P0,可以将在optional文件夹中c3p0文件夹下的包都导入。
5.导入Hibernate之外的包:
6.导入包之后,运行环境就基本搭建完毕了。后面都是具体需求具体配置了。
这个基础示例作用是让你了解Hibernate的使用需要做什么?先让你了解需要做什么,后面再讨论每个里面能具体怎么做。
1.运行环境搭建,导入需要的依赖包。
2.创建持久类
public class Person { private Long pid; private String username; private Integer age; private Integer address; public Long getPid() { return pid; } public void setPid(Long pid) { this.pid = pid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getAddress() { return address; } public void setAddress(Integer address) { this.address = address; } }
3.创建映射文件Person.hbm.xml
【这里还有个可选操作:创建数据表,数据表Hibernate也可以帮你创建(十分建议提前创建,不然你可能会遇到其他问题),这里就省略了。当然,数据库应该是已经创建好了的。但我已经踩过坑了,所以我这里就懒了。】
4.创建核心配置文件:hibernate.cfg.xml
5.读取核心配置文件
6.与数据库交互
public class Person { private Long pid;//唯一标识属性,用来映射表中的主键 private String username; private Integer age; private Integer address; public Long getPid() { return pid; } public void setPid(Long pid) { this.pid = pid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getAddress() { return address; } public void setAddress(Integer address) { this.address = address; } }
1.持久化类的属性尽量使用包装类类型。【因为基础数据类型通常存在默认值并且通常不为null,null在数据表中是空的意思,但如果是0的话,意思就是不明确了--比如某个字段用0,1代表某种状态,那么默认值为0的话就不好知道是否是忘记设置状态了。】
2.持久化类不要使用final修饰,不然无法使用延迟加载功能(这个东西将会在后面的CRUD中的查询中讲),延迟加载功能依赖于代理对象,使用final修饰之后就无法产生代理对象了。
核心配置文件的配置方式有使用properties和xml这两种,我们主要使用xml版,因为xml版可以导入映射文件,而使用properties版的就需要自己在代码中引入映射文件。
那么,去哪里看能配什么,配哪些值呢?
在解压包的project的etc下面有一个hibernate.properties,里面有hibernate的核心配置信息。想使用什么功能,ctrl+f查找一下即可。
首先需要配置hibernate的连接提供者:
然后配置c3p0的选项:
<!-- 配置C3P0连接池 --> <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property> <!-- 最少可用连接数 --> <property name="hibernate.c3p0.min_size">5</property> <!-- 最大可用连接数 --> <property name="hibernate.c3p0.max_size">20</property> <!--设定数据库连接的过期时间,以秒为单位 --> <property name="hibernate.c3p0.timeout">120</property>
最后,要记得导入c3p0的依赖包,它在hibernate文件夹的optional的c3p0中。
映射文件的结构大概如下:
1. 映射文件名要求:类名.hbm.xml
2. 标准xml首部:<?xml version="1.0" encoding="UTF-8"?>
3. dtd约束:对于映射文件,它的dtd可以在中获取。打开文件,你能看到dtd写在了注释里面。hibernate5与hibernate3的相似,通常使用的都是3.0版本的。
4.创建标签hibernate-mapping
后续的内容都在hibernate-mapping中配置,下面讲hibernate-mapping怎么配置:
id标签用来配置主键,generator标签是用来配置主键生成策略的(主键的值的策略),generator的class的值常用的有如下几个
上面已经讲述了配置核心配置文件,配置映射文件,下面就是与数据库的交互了。要与数据库交互要了解以下几个类
了解上面对象的作用与使用之后,我们就可以得出以下与数据库建立连接并交互的步骤:
了解了怎么建立与数据库的连接之后,下面就是CRUD的内容了。
1.创建持久类对象
2.session对象调用save(Object obj)来保存到数据库
查询是一个很重要的内容,考虑到这篇博文已经很长了的,所以我留到另外一篇博文去讲述了。
详情点击下面超链接。
【这里给的例子使用的查询是OID查询,更多查询方法请查看"查询"那里的用法】
public void test1() { Configuration cfg = new Configuration().configure(); SessionFactory factory = cfg.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); Person person = session.get(Person.class, 2L); person.setUsername("葫芦娃"); session.update(person); transaction.commit(); }
【这里给的例子使用的查询是OID查询,更多查询方法请查看"查询"那里的用法】
public void test2() { Configuration cfg = new Configuration().configure(); SessionFactory factory = cfg.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); Person person = session.get(Person.class, 2L); session.delete(person); transaction.commit(); }
缓存:是一种优化的方式,将数据存入到内存中,使用的时候直接从缓存中获取,不用通过存储源。
瞬时态:新建的持久类,唯一标识属性没有值的时候,没有被session管理的时候。
持久态:唯一标识属性有值,被session管理的时候(要么是用session从数据库中查询出来的;要么是已经使用过session保存到数据库中,并且还没有被删除的。)。持久态的对象会存储到缓存区中,用来提高效率。
脱管态:唯一标识属性有值,不被session管理的时候。
持久类状态转换图:
开始事务:Transaction transaction = session.beginTransaction();
事务提交:transaction.commit();
事务回滚:transaction.rollback();
事务隔离级别配置要在核心配置文件中配置:
<property name="hibernate.connection.isolation">4</property>
值可以为:
1:Read Uncommitted
2:Read Committed
4:Repeatable Read
8:Serializable
使用Hibernate之后,操作数据库转成了操作一个个对象,当在一个函数中操作对象中,事务管理可以直接在函数中使用Transaction来管理。
但在使用Hibernate来实现DAO层的功能的时候,如果你把功能细化了,比如加钱是一个函数,减钱是一个函数。当需要考虑到事务管理的时候,你可能需要把一个session传入到这两个函数中才能实现事务管理。你可能会想到把session作为形参传进去,这是一个解决方法。但Hibernate提供了一个更好的解决方案。
这时候我们可以使用getCurrentSession来获取session,getCurrentSession是通过当前线程来获取一个session,同一个线程获取的session是同一个,所以同一个线程执行不同的函数依然可以很好的进行事务管理。
所以很多时候,factory.getCurrentSession()取代了Session session = factory.openSession()。
getCurrentSession的正常使用需要核心配置文件加上一个配置选项:
<property name="hibernate.current_session_context_class">thread</property>
在上面的基础示例中,演示的是一个单表的例子。而事实上,项目中的表与表之间是有关系的。并且我们经常需要利用这些关系来获取数据。
【为了帮助理解,步骤与演示一起讲述,引用框里面不是引用,是例子演示。以班级与学生之间的一对多关系(假设一个班级可以有多个学生,一个学生只能有一个班级)为例】
1.创建“多”方的类,在普通的单表的基础上要多出一个“多”一方的类的对象,这个类的对象是用来映射成外键字段的,所以这个类不需要另外创建映射到外键的“属性”(一对多都是基于外键,不使用Hibernate的时候,可能需要手动去创建外键外键和手动去给外键指定值;但这里使用了Hibernate之后,它会自动映射成外键。)。
建立学生类Student,添加一个班级类Grade的对象--用来映射学生所对应的班级;
2.创建“一”方的类,在普通的单表的基础上要多出一个“一”一方的对象组成的集合(这里最好提前new一下,免得后面再去赋值,new了之后,后面就可以直接get来add了)。这个集合是用来帮助Hibernate处理它们之间的关系的,不会在表中生成新的字段。
建立班级类Grade,添加一个set集合,用来映射班级所对应的多个学生
3.建立"多"一方的hbm,像单表配置一样配置完“多”一方的属性(除去那个“一”一方的对象);配置剩下的那个“一”一方的对象需要使用many-to-one标签来配置,name属性是那个“一”一方的对象的变量名,class属性是“一”一方的类的全路径,column属性是“一”一方的对象要映射成的外键的字段名(这个字段名会出现在“多”一方的表中)。
建立Student.hbm.xml:
4.建立“一”一方的hbm,像单表配置一样配置完“一”一方的属性(除去那个“多”一方的集合对象);配置剩下的那个“多”一方的集合对象需要使用set标签来配置,name属性是那个“多”一方的集合对象的变量名,key中的column属性是”多“一方的外键字段名。one-to-many的class属性是“多”一方的类的全路径。
Grade.hbm.xml
5.在hibernate.cfg.xml中导入映射文件
导入映射文件
6.编写测试类,将”一“的一方的对象和”多“的一方的对象互相建立上关系(把一一方的对象设置到多的一方的对象的属性中,把多的一方的对象添加到一的一方的集合中),然后保存起来。
创建两个学生(李白、杜甫)、一个班级(唐朝);给学生的grade属性设置值,将两个学生加入到班级的学生集合中;把三个对象都保存起来。
public void test3() { Configuration configure = new Configuration().configure(); SessionFactory factory = configure.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); //创建两个学生一个班级 Student s1=new Student(); Student s2=new Student(); s1.setName("李白"); s2.setName("杜甫"); Grade g=new Grade(); g.setCname("唐朝"); //设置关系 s1.setGrade(g); s2.setGrade(g); g.getStudents().add(s1); g.getStudents().add(s2); //保存 session.save(s1); session.save(s2); session.save(g); transaction.commit(); }
(假设一方为A,一方为B)
1.创建A的持久类,里面有一个B类对象组成的集合【例子演示以一个商品可以属于多个购物车,一个购物车可以有多个商品为例。】
创建购物车类ShopCar:
2.创建B的持久类,里面有一个集合,
创建产品类Product:
3.创建A的hbm,使用property配置好基本属性,使用set配置配置集合:set中的name的值是自身存储的对方对象的集合的变量名,table是中间表的名称(自动创建);key中的column的值是自身在中间表映射的外交字段名;many-to-many中class的值是对方类的全路径,column的值是对方类在中间表映射的外键字段名。
创建ShopCar.hbm.xml:
4.创建B的hbm,像第三步一样配置好
创建Product.hbm.xml:
5.在hernate.cfg.xml中导入映射文件
导入Product.hbm.xml和ShopCar.hbm.xml
6.编写测试类,将一个A类对象与多个B类对象建立关联,将一个B类对象与多个A类对象建立关联,把所有A和B的对象保存,查看中间表的数据。
编写测试类,建立两个Product,建立两个ShopCar,让他们之间相互都建立起关联。
public void test4() { Configuration configure = new Configuration().configure(); SessionFactory factory = configure.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); //新建两个订单,两个产品 ShopCar o1=new ShopCar(); ShopCar o2=new ShopCar(); o1.setCustomer("马云"); o2.setCustomer("马化腾"); Product p1=new Product(); Product p2=new Product(); p1.setProduct_name("聚宝盆"); p2.setProduct_name("招财猫"); //相互建立关系 o1.getProducts().add(p1); o2.getProducts().add(p1); o1.getProducts().add(p2); o2.getProducts().add(p2); //保存 session.save(o1); session.save(o2); session.save(p1); session.save(p2); transaction.commit(); }
什么是级联操作?当表之间有关系的时候,对一个表做操作(删除、修改),对应去更改关联的表。【注意!这里的关系相对于表而言,但对于对象来说,是对象中是否存储着另一方的对象(当然还需要映射关系的帮助),所以只有关联上的才会级联,没有关联上的不会级联】
一对多的级联操作设置:多中的set中的cascade,一中的many-to-one中的cascade
<many-to-one name="grade" class="work.domain.Grade" column="gid" cascade="save-update"></many-to-one>
多对多的级联操作设置:set中的cascade
<set name="shopcars" table="shopcar_product" cascade="save-update"> <key column="product_id"></key> <many-to-many class="work.domain.ShopCar" column="shopcar_id"></many-to-many> </set>
在学生hbm中加上cascade="save-update":那么只保存学生对象,也可以同时保存学生对象所管理的班级对象。
<many-to-one name="grade" class="work.domain.Grade" column="gid" cascade="save-update"></many-to-one>
public void test5() { Configuration configure = new Configuration().configure(); SessionFactory factory = configure.buildSessionFactory(); Session session = factory.openSession(); Transaction transaction = session.beginTransaction(); //创建两个学生一个班级 Student s1=new Student(); s1.setName("林则徐"); Grade g=new Grade(); g.setCname("清朝"); //设置关系 s1.setGrade(g); g.getStudents().add(s1); //保存 session.save(s1);//只保存学生,可以同时保存这个学生对象所关联的班级对象 transaction.commit(); }
题外话:对于一对多级联操作(假设一一方为A,一方为B),可能会产生多余SQL语句,原因是缓存区中A,B的数据都发生了变化了,使得不与快照区的数据映像相同,Hibernate默认就会对更新了的对象进行修改处理。
在新建的时候,不涉及到快照区,所以不会发生上述问题;在修改外键的时候就会发生,比如先查询出来一个A,两个B(数据会存储到缓存区和快照区),A原本仅仅与B1关联,现在增加了与B2的关联,那么这时候缓存区中的A对象发生了数据更改,B2对象也发生了更改。在提交的时候,校验缓存区和快照区的数据发现缓存区的A和B2与快照区的数据不同了,这时候hibernate会发起对A的数据更新(外键新增)以及对B2的数据更新(外键新增)。所以发起了两次外键新增,这就产生了多余的SQL语句。【而多对多中,Hibernate考虑到如果两个都有的话,会在中间表插入重复的,所以多对多不会发生这个问题(测试过!)】
解决方案:inverse是Hibernate定义的一种维护外键的属性,当这个属性设置为true时,那么设置的一方会放弃外键维护权(代表外键更新了它也不会去管理),对于一对多,通常都是在set上配置inverse=”true”,让多的一方去维护外键 。
如果你已经导入前面说讲的那几个日志包了的话,那么你还需要一个log4j.properties就可以配置好你的日志功能了。
至于log4j.properties怎么写,project/etc中有一个log4j.properties,可以参照那个来写。
# # Hibernate, Relational Persistence for Idiomatic Java # # License: GNU Lesser General Public License (LGPL), version 2.1 or later. # See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. # ### direct log messages to stdout ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n ### direct messages to file hibernate.log ### #log4j.appender.file=org.apache.log4j.FileAppender #log4j.appender.file.File=hibernate.log #log4j.appender.file.layout=org.apache.log4j.PatternLayout #log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n ### set log levels - for more verbose logging change ‘info‘ to ‘debug‘ ### log4j.rootLogger=warn, stdout #log4j.logger.org.hibernate=info log4j.logger.org.hibernate=debug ### log HQL query parser activity #log4j.logger.org.hibernate.hql.ast.AST=debug ### log just the SQL #log4j.logger.org.hibernate.SQL=debug ### log JDBC bind parameters ### log4j.logger.org.hibernate.type=info #log4j.logger.org.hibernate.type=debug ### log schema export/update ### log4j.logger.org.hibernate.tool.hbm2ddl=debug ### log HQL parse trees #log4j.logger.org.hibernate.hql=debug ### log cache activity ### #log4j.logger.org.hibernate.cache=debug ### log transaction activity #log4j.logger.org.hibernate.transaction=debug ### log JDBC resource acquisition #log4j.logger.org.hibernate.jdbc=debug ### enable the following line if you want to track down connection ### ### leakages when using DriverManagerConnectionProvider ### #log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace
写在最后:Hibernate毕竟是一个框架,市面上的书写它都能写它几百页,在一篇博文内不可能把内容写得详尽。所以这篇博文很大程度上主要是你让你了解Hibernate,剩下的就要你自己去了解了。获取百度才是你最好的老师?
这篇博文断断续续写了三天,很多时候都是在想以什么一种方式来讲述才能更好地让大家理解。最终成了这个样子,希望能帮到大家。
标签:特殊 inf 需求 帮助 provider 名称 映射文件 同名 协助
原文地址:https://www.cnblogs.com/progor/p/9393665.html