话题一:hibernate的基本使用 学习方向:重点理解hibernate是什么,然后牢记其基本的编程步骤 一:hibernate 是什么? 1: hibernate 是对JDBC进行了封装,负责对象持久化,位于中间层,在应用程序和数据库之间起到了桥梁作用的一种映射工具。(映射是通过配置文件来维护的) 二:为什么使用hibernate? 1:对象持久化的概念: 把内存中的数据转存到外部持久设备 2:对象持久化的原因: (1)内存不能长时间的保留data (2)内存容量有限 (3)需要数据共享 (4)业务需要数据能够格式转化,支持数据检索 3:使用hibernate的原因 (1):使用hibernate,应用程序层就可以专注于业务逻辑的实现,只需要知道怎么使用hibernate 所提供的接口,而不用知道具体实现的细节。 (2):封装了JDBC之后,就可以连接数据库时减少代码的冗余。 (3):直接用映射文件,可以方便的实现与不同数据库连接。 三:hibernate 的使用 1: DTD文件的作用和配置 在写配置文件的时候检验缩写的配置文件的合法性 2:配置文件 (1):全局配置文件:hibernate.cfg.xml: 主要用来实现整个应用程序中使用的数据库,以及这个应用程序中的对象关系映射。 例子: <?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "/home/soft01/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="connection.driver_class"> com.mysql.jdbc.Driver </property> 指定数据库的驱动程序 <property name="connection.url">jdbc:mysql://127.0.0.1:3306/test </property> 指定连接数据库的URL <property name="connection.username">root </property> 联结数据库的用户名, <property name="connection.password">831022</property> 密码 <propertyname="dialect">org.hibernate.dialect.MySQLDialect </property> 指定数据库使用的方言,本质上就是hibernate API 中的一个用来标示不同数据库的接口 <property name="show_sql">true</property> 用来在控制台输出hibernate 产生的SQL语句 <property name="format_sql">true</property> 对SQL语句进行format <mapping resource="Student.hbm.xml"/> 对象关系映射文件,可以有多个 </session-factory> </hibernate-configuration> 使用注意: ◆ 任何一个应用程序的全局配置文件名字都是hibernate.cfg.xml,并且默认放在工程的根目录下,也就是与源程序代码同级。每一个工程只能有一个这样的全局配置文件,如果想要使用别的配置文件,调用configuration 的有参的构造方法,指定这个配置文件的位置。 (2):对象---关系映射文件: ◆命名规则:className.hbm.xml ◆ORM的具体细节 Class to Table:类对表 Properties to columns:属性对字段 Object to item: 对象对记录 ◆作用及应用:用来使实体类对象(class)与数据库的表产生一一对应关系 ◆使用注意事项: (1)建表的时候一般会使用一个跟业务逻辑没有关系的字段作为这个表的主键。 (2)这种映射文件可以一个类一个,也可以把多个类的映射关系写在一个关系映射文件中,但是推荐使用前者,因为这样在开发的过程中便于区分管理。 (3)该文件最好同实体的类文件放在一起.并且即使配置文件和实体类都在同一个包下面,在配置文件里面也要加上包;并且包里面的路径分割符号为:”.” 3:hibernate 编程七步曲 (1)Configuration config = new Configuration() 根据默认的位置hibernate的配置文件,创建一个configuration对象, 如果不想使用默认的文件,那么需要调用config对象的有参构造,来指定配置文件的位置。 (2)SessionFactory factory =config.configure().buildSessionFactory(); 调用它的configure()方法来解析hibernate.cfg.xml配置文件。然后通过解析这个文件来建立与特定数据的连接。并且从里面的mapping-resource 里面配置的文件,来进行关系影射。 (3)Session session=factory.openSession(); //应用程序与数据打交道的一个对象 (4)Transaction trans = session.beginTransaction(); (5)通过session对象操作数据库 session.delete(); session.save() session.saveOrUpdate() session.update() session.load(); (6)trans.commit(); (7)关闭资源 ◆使用注意: (1)在创建SessionFactory对象后,如果再修改配置文件,那么后边的操作不回被影响,因为创建SessionFactory对象的时候已经把原来的配置文件信息加载到sessionfactory的缓存中了,sessionfactory是一个重量级的对象,一般在产生一个之后不要随便销毁,因为创建和销毁一个sessionfactory 对象需要消耗很多的资源 (2)session是一个轻量级的对象,线程不安全的,所以应该一个请求使用一个单独的session,使用完就关闭,当然开始一个事务的时候也要先创建一个这样的对象。 小插曲:threadlocal,是一个只在当前线程有效的一个变量。也即不同线程拥有的这个变量不同。(看老师代码,使用方式) 话题二: hibernate 中对象id的生成方式: 一:主键生成策略大体分类: 1:hibernate 负责对主键ID赋值 2:应用程序自己为主键ID赋值(不推荐使用) 3:底层数据库为主键ID赋值 二:具体用法 1:increment:(跨平台的) 以递增的方式为代理主键赋值,每次维护的当前实例中取出一个最大值,在最大值的基础之上再进行加法,适合只有单个应用进程访问数据的情况,否则会产生并发冲突问题(主键重复)。并且oid 的类型必须是long ,int ,short类型的。 2:assigned :由程序员给赋值,(不建议使用) 3:identity:是由底层的数据库来生成主键,他要求底层数据库支持自动增长,并且把主键定义成自动增长类型的 ,oid 必须是long ,int ,short 类型的。 Oracle 不支持。Mysql 支持 4:sequence:由底层的数据根据序列来生成主键。oracle ,db2数据库支持,mysql不支持 5:native:(跨平台的) 由hibernate 自己跟据不同的数据库的主键生成方式,自己选择生成方式(可以跨平台) 6:hilo :(跨平台的),通过hi/lo算法实现主键的生成机制,但是需要额外的表来保存主键的生成历史。 使用规则: 通用性和安全性: 7:seqhilo:取得sequence的值,来生成高位,产生id。 但是只能使用于有sequence的数据库(oracle , db2); 8:uuid.hex: 用系统时间和ip地址等具有天然唯一性的资源进行计算,生成id, 全球唯一。生成的是一个128位的二进制数。然后把这个二进制数,转化成一个16进制的数(32位),再转化为字符串付给我们的id。那么我们的实体类的oid以及数据库的主键都需要随之改为字符串类型的。 9:foreign 用于共享主键的时候。(一对一主键关联); 话题三:hibernate中对象的状态及数据类型 一:持久化对象的状态 1:session同步的机制 (1)Session有一个缓冲区。在对象进入持久化状态时,同时在session缓冲区中保存一个这个对象的副本,当刷新缓冲的时候,会将这个副本和内存的对象进行比较,如果发现有不同,则进行同步。 (2)Session:默认情况下自动同步的时机: 对象状态的变化,并不会立即提交给数据库,这就允许hibernate对需要进行的操作,进行优化,生成尽可能少sql操作。在以下情况下会进行同步 ●事务提交,(本质上也是促使了session缓冲区的刷新) ●刷新session缓冲区 ●某些查询语句调用前(内存中和db中状态不一样的时候的查询) (3)session同步时机的设置 FlushMode.AUTO: 默认的,在以上三个时候都会刷新 FlushMode.COMMIT:只有当提交或者明确调用flush()时 FlushMode.NEVER:只有明确调用flush()方法是,才刷新。 2:一个对象的状态转换 注:无名状态的含义是,db中有,内存中没有。 (1):临时状态:(DB中没有记录,和session无关) 不受session 管理的,在对象中可表现为持久化对象的主键没有值 save() (2):持久化状态:(DB中有记录,和session有关) 进入了session 的管理,session会自动进行同步。 (3):游离状态(托管):(DB中有记录,和session无关,需手工进行同步) 脱离了session 的管理。有与数据库中对应的主键,所以还可以进入持久化状态。 (4):无名状态:(DB中有记录,内存中没有) 话题四:基数类型的对象关系映射:(重点,难点) 一:一对一关系: 1:外键关联(一个订单(order)只有一个送货地址(address)) (本质上是一个一对多的双向关联,只是在多的一方用unique限制)。 (1)SQL语句 create table address( aid number(11) primary key , street varchar2(20) not null, zipcode varchar2(20) not null ); create table order( oid number(11) primary key, name varchar2(20)not null, aid number(11) unique references address(aid) ); (2)配置文件: order.hbm.xml hibernate-mapping package="day02.onetoone.fk"> <class name="Order" table="ORDER"> <id name="oid" column="oid"> <generator class="hilo"> <param name="table">hilo</param> <param name="column">hi</param> </generator> </id> <property name="name" column="name"></property> <many-to-one name="addr" column="aid" unique="true" cascade="all"></many-to-one> </class> </hibernate-mapping> address.hbm.xml <hibernate-mapping package="day02.onetoone.fk"> <class name="Address" table="ADDRESS"> <id name="aid" column="AID"> <generator class="hilo"> <param name="table">hilo</param> <param name="column">hi</param> </generator> </id> <property name="street" column="street"></property> <property name="zipcode" column="zipcode"></property> </class> </hibernate-mapping> 2:共享主键 (1)SQL语句 create table address_pk( aid number (11) primary key, street varchar2(20) not null, zipcode varchar2(20)not null ); create table order_pk( oid number(11) primary key references xuzheng_address_pk(aid), name varchar2(20) not null ); (2)配置文件 Order.hbm.xml <hibernate-mapping package ="day02.onetoone.pk"> <class name="Order" table="ORDER_PK"> <id name="oid" column="oid"> <generator class="foreign"> <param name="property">addr</param> </generator> </id> <property name="name" column="name"></property> <one-to-one name="addr" constrained="true" cascade="all"></one-to-one> </class> </hibernate-mapping> Address.hbm.xml <hibernate-mapping package="day02.onetoone.pk"> <class name="Address" table="ADDRESS_PK" lazy=”true”> <id name="aid" column="AID"> <generator class="hilo"> <param name="table">hilo</param> <param name="column">hi</param> </generator> </id> <property name="street" column="street"></property> <property name="zipcode" column="zipcode"></property> </class> </hibernate-mapping> 使用注意: (1) 对于作为外键同时是主键的那个影射文件,它的ID的生成方式必须是foreign. (2) constrained(约束) 表明该类对应的表和被关联的对象所对应的表之间,通过一个外键引用对主键进行约束。这个选项影响save()和delete()在级联执行时的先后顺序,到了hibernate 3.0之后是可选的,可写可不写, (3)延迟加载:想要在one to one 关系中实现延迟加载。仅有lazy=”true”是不够的,还需要加上constrainted=“true”;才能实现。 二:一对多关系(一个公司有多个员工) 应用场合:一个类中的某个属性,是另外一个类的集合。 1: SQL语句 create table company( id number(11) primary key , name varchar2(20) ); create table employee( id number(11) primary key , name varchar2(20)not null, fid number(11) references company(id) ); 2:配置文件 (1)company.hbm.xml <hibernate-mapping package="day02.onetomany"> <class name="Company" table="company"> <id name="id" column="ID"> <generator class="hilo"> <param name="table" >hilo</param> <param name="column">hi</param> </generator> </id> <property name="name" column="name" ></property> <set name="emloyee" table=" employee" cascade="save-update" inverse="true" > <key column="fid" ></key> <one-to-many class="Employee" ></one-to-many> </set> </class> </hibernate-mapping> (3)employee.hbm.xml <hibernate-mapping package="day02.onetomany"> <class name="Employee" table=" employee"> <id name="id" column="ID"> <generator class="hilo"> <param name="table" > hilo</param> <param name="column">hi</param> </generator> </id> <property name="name" column="name" ></property> <many-to-one name="company" column="fid" cascade="save-update" ></many-to-one> </class> </hibernate-mapping> (4)使用注意: 在一对多的双向关联关系中,关系的维护都应该由多的一方维护,所以在一哪方把inverse 属性设为true.在数据库操作的时候,可以执行较少的语句,从而能够提高效率。否则在有外键的一方总是先插入一条语句,然后再执行Update()操作,才能完成真正的插入。 三:多对多关系(一个学生可以选多门课程,一门课程可以由多个学生选择) 1: SQL 语句 create table student( sid number(11) primary key , name varchar2(20) not null, stuid varchar2(20) unique not null ); create table cource( cid number(11) primary key , name varchar2(20) not null, courId varchar2(20) unique not null ); create table middle( fsid number(11) references student(sid), fcis number(11)references cource(cid), constraint mtom primary key (fsid ,fcid ) 2:配置文件 (1)student.hbm.xml <hibernate-mapping package="day03.manytomany"> <class name="Student" table=" student"> <id name="sid" column="SID"> <generator class="native"> </generator> </id> <property name="name" column="name" ></property> <property name="stuid" column="stuid" ></property> <set name="cource" table=" middle" cascade="save-update" > <key column="fsid" ></key> <many-to-many class="Cource" column="fcid" ></many-to-many> </set> </class> </hibernate-mapping> (2)cource.hbm.xml hibernate-mapping package="day03.manytomany"> <class name="Cource" table=" cource"> <id name="cid" column="CID"> <generator class="native"> </generator> </id> <property name="name" column="name" ></property> <property name="courceId" column="courceid" ></property> <set name="student"table="middle"cascade="save-update"inverse="true" > <key column="fcid" ></key> <many-to-many class="Student" column="fsid" ></many-to-many> </set> </class> </hibernate-mapping> 使用规则: 在多对多关系关系中,都是双向关联,那么双方关系的维护放在任何一方都可以。 (1)Inverse:可以放在任意一方,但是不能两边都放,否则中间表就没有关联上。 (2)在删除的时候,如果两放的cascade=“save-update”, 那么就不能删除inverse=”true”方。 (3)双方不能同时cascade=”all”,要不删除任意一方的时候,都会把两个表以及关联表中的数据都删掉 (4)如果inverse=”true”所在的级联关系是cascade=“save-update”,另一方是cascade=“all”,那么双放就都不可以删除 (5)如果inverse=”true”所在的级联关系是cascade=“all”,另一方是cascade=“save-update”,那么删除inverse=”true”所在的放一方,就会同时把中间表和另一方表的内容删掉。 总结一句话就是: 在多对多的映射是,删除的时候,尽量不要删除inverse=”true”所在的一方,同时两边的关联关系都设置为save-update”,其他的关系,在删除的时候都会出问题(不能删除,或者删除的时候跟业务逻辑不符合) 在设置表关系的时候,尽量避免使用多对多,用一对多代替。 话题五:组件关系映射:(一对一关系强烈) 一:单一组件映射(一个car一个engine): 1:SQL语句 create table car( id number(11) primary key , name varchar2(20) not null, num2 varchar2(20) not null, //以下是engine的属性,engine类没有单独的表 num1 varchar2(20) not null, typee varchar2(20) not null, datee date not null ) 2:配置文件 <hibernate-mapping package="day04.componet"> <class name="Car" table=" CAR"> <id name="id" column="id"> <generator class="hilo"> <param name="table"> hilo</param> <param name="column">hi</param> </generator> </id> <property name="name" column="name"></property> <property name="num2" column="num2"></property> <component name="engine" class="Engine"> <property name="num1"></property> <property name="typee"></property> <property type="date" name="datee"></property> </component> </class> </hibernate-mapping> 3:特点概述 (1)使用场合:两个类之间的关联关系比较强烈,并且附属对象的属性比较少,且冗余少的情况。 (2)优点:只需要建一张表,只需要写一个配置文件。查询效率高。 二:集合组件映射(一个账户的注册地址可以多个) 1:sql语句 create table components_account( oid int(10) primary key auto_increment, actno varchar(20) not null unique, balance double(12,2) , ); //注意,address表不再含有主键,只是有一个外键 create table components_address( fid int(10) references components_account(oid), zipcode varchar(20) not null unique, city varchar(20) not null, street varchar(20) not null ); 2:配置文件片段 <hibernate-mapping package="day04.components.entity"> <class name="Account" table="components_account"> <id name="oid" column="OID" > <generator class="native"></generator> </id> <!-- account的其他基本属性省略-- > <set name="addrs" table="components_address"> <key column="fid" /> <composite-element class="Address"> <property name="zipcode" column="ZIPCODE" unique="true" not-null="true" /> <property name="city" column="CITY" /> <property name="street" column="Street" /> </composite-element> </set> </class> </hibernate-mapping> 3:特点概述 (1)使用场合:一对多关系强烈,附属类的属性都是基本属性,并且不需要维护到主类之间的关系,不是一个独立的实体类(也即对应的表没有主键oid,但是有外键来约束主类的主键) (2)数据库特点:集合组件映射,只用一个表。 (3)一对多关系强烈时候的首选 话题六:继承关系映射 一:table-per-concreate-class 一个子类建一张表; 1:特点:即父类没有表,每一个子类单独对应一个表,不支持多态查询,操作时只对单表 2:配置文件片段: <union-subclass name="CreditAccount" table="entity_creidt"> <property name="credit" column="" not-null="true"> </property> </union-subclass> 3:缺点: 修改父类的时候,其具体的子类所对应的表字段也都要随之改变; 不支持多态,一个支持多角色,则保持数据完整性很困难。 4:优点:简单易用,因为一个具体类只存在一张表中。 二:table-per-class-hierarchy :将整个继承层次写一张表 1:特点:子类和父类都在同一张表中,支持多态查询 , 2:配置文件片段 <subclass name="CreditAccount" discriminator-value="c"> <property name="credit" column="credit" ></property> </subclass> 3:缺点:会有大量的null字段,并且子类的属性不能有非空限制。 4:优点:只使用一个表格,查询时只需一次简单的sql语句 5:使用场合:需要多态且子类属性较少的时候适用 三:table-per-subclass 一个类写一张表,包括父类 1:特点:子类和父类各自对应自己的一张表。 2:配置文件片段; <joined-subclass name="DebitAccount" table="inhreit_all_debit"> <key column="oid"></key> <property name="currency" column="currency" not-null="true"> </property> </joined-subclass> 3:优点:很好的遵循了面向对象的思想,支持多态,只需要在合适的表中拥有角色所需要的字段即可。修改继承树中的父类和子类都很容易,因为只需要修改一个类或一个表。 4:缺点:有太多的数据表,每张表对应一个类(在多对多关系中还需要用一张额外的表去维护关系); 查询的时候要多表查询,读取数据的时间较长。解决办法是将数据库中的数据组织到不同的磁盘面上(假定每一个磁头都是独立的操作)。这种模式的读写性能问题将会得到一定的缓解。 5:使用场合: 需要多态且子类属性较多的时候适用 话题七:值类型的集合影射: 一:集合映射: 1:set :无序的,不能有重复元素 (1):sql语句 create table person( id number(11) primary key , name varchar2(20) not null); create table aliases( aid number(11) references person(id), aliase varchar2(20), constraint aliases primary key(aid ,aliase) ); (2)配置文件 <hibernate-mapping package="day02.collection"> <class name="Person" table=" person"> <id name="id" column="ID"> <generator class="hilo"> <param name="table" > hilo</param> <param name="column">hi</param> </generator> </id> <property name="name" column="name"></property> <set name="aliases" table=" aliases" > <key column="aid" ></key> <element type="string" column ="aliases"></element> </set> </class> </hibernate-mapping> 2:bag 无序的,允许有重复元素 (1)sql语句 create table direction( id number(11) primary key , name varchar2(20) not null ); create table xuzheng_file( cid number(11) not null, filename varchar2(50) not null, fdid number(11) references xuzheng_direction(id), constraint bag primary key (cid ,fdid) ) (2)配置文件 <hibernate-mapping package="day04.bag"> <class name="Direction" table="DIRECTION"> <id name="id" column="id"> <generator class="hilo"> <param name="table"> hilo</param> <param name="column">hi</param> </generator> </id> <property name="name" column="name"></property> <idbag name="files" table="file"> <collection-id type="integer" column="cid"> <generator class="increment"></generator> </collection-id> <key column="fdid"></key> <element column="filename" type="string"></element> </idbag> </class> </hibernate-mapping> 3:list:有序的,允许有重复元素 (1):sql语句 create table word( id number(11) primary key , name varchar2(20) not null ); create table descs( fwid number(11) references word(id), descs varchar2(50) not null, pos number(11) not null, constraint list primary key (fwid,pos) ); (2)配置文件 <hibernate-mapping package ="day04.list"> <class name="Word" table="WORD"> <id name="id" column="id"> <generator class="hilo"> <param name="table">hilo</param> <param name="column">hi</param> </generator> </id> <property name="name" column="name"></property> <list name="descs" table="descs"> <key column="fwid"></key> <list-index base="0" column="pos"></list-index> <element column="descs" type="string"></element> </list> </class> </hibernate-mapping> 4:map: (1):sql create table dictionary( id number(11) primary key , name varchar2(20) not null ) create table words( fdid number(11) references dictionary(id), name varchar2(50) not null, descs varchar2(20)not null, constraint map primary key (fdid,name) ); (2):配置文件 <hibernate-mapping package="day04.map"> <class name="Dictionary" table=" DICTIONARY"> <id name="id" column="id"> <generator class="hilo"> <param name="table">hilo</param> <param name="column">hi</param> </generator> </id> <property name="name" column="name"></property> <map name="words" table="words"> <key column="fdid"></key> <map-key type="string" column="name"></map-key> <element column="descs" type="string"></element> </map> </class> </hibernate-mapping> 话题八:一些小知识点 一:hibernate中的数据类型 Hibernate中的数据类型,不是一个单独的新数据类型,而是java和db中数据类型的一种映射。 二:小插曲hql Hql 语句:hibernate query language 面向对象的语言,里面的查询对象是对象,判断条件是类的属性。 (1)等值查询 from Account(类名) a where a.bal(类属性)=? (2)模糊查询(like 查询) from Account a where a.actno like act-00%; (3)范围查询(in 查询) List params =new ArrayList(); parames.add(“ “); from Account a where a.actno in (:actlist); query.setParameterList(params); (4)做报表查询的语句 select new day05.NamePair(s.name,c.name) from Student s join s.cours c; 注:day05.NamePair是自己定义的一个VO(value object);要加上包名。 (5)其他查询,大于,小于,or等都跟sql语句是一样的。 三:锁机制: 1:锁的分类: (1)悲观锁: 事务一访问一个数据的时候,别的事务不能进行任何操作, 实际上第二个事务不一定存在,只是第一个事务悲观的一种想象。牺牲了效率来达到数据安全的目的。 (2)乐观锁 在读取数据库中数据的时候,把读取的数据加上版本号,往数据库里面存的时候,只有当数据库中版本号小于要插入数据的版本号的时候,才能操作成功。 需要在pojo对象里面加一个version属性,在对应的数据库表中加一个version字段,配置文件中也需要配置。 四:标签属性: 1:Cascade: (设置级联属性) 用在<many-to-one>,<set>,<one-to-one>标签中。 (1)Save –Update::两个对象在存储和修改的时候级联。主要应用于一对一,一对多关系中一的一方。 (2)Delete:只要有级联关系,那么删除一个对象的时候,删除所有与之有级联关系的对象。 (3)All:所有操作(delete和save-update),都有级联。 (4)delete-orphan: 删除所有和当前对象解除关联关系的对象。 (5)All-delete-orphan:两个类之间有父子关系(即父亲不存在了,儿子的存在已经没有意义了,主要用于从集合中删除对象的情况。包含all和delete-orphan两个要求 2:inverse:(确定关系维护者) (1)Inverse=“true”:把双方关系的维护交给对方,否则自己维护。主要用在一对多,和多对多关系中, (2)在一对多的关系中,把关系维护交给多的一方,即在一的一方,设置:inverse=”true”, (3)在多对多的关系中,随便哪方都可以:但是不能同时都设置。 (4)主要目的是:在插入记录的时候,直接就执行了Insert语句,而不用先插入空,再执行update语句。提高了执行效率 3:lazy=”true” (延迟加载,默认就是true) (1)本质:延迟加载,只取需要的,提高效率。 (2)例如:User 中有一个集合属性Account,在表结构中,二者是有关联关系得两个表,那么当调用 session.load(student)/get(student) 方法时,只是先把User这条记录取出,而Account 的取出,则在第一次使用的时候才加载。 注意:在一对一主键关联的时候,还需要配合constraited=“true”来共同实现延迟加载。 (3)疑问(部分参考书说法不一): 经测试:一对一关系中默认是:lazy=”false”,而在一对多,和多对多中是lazy=”true”;