java中操作数据库唯一技术:JDBC技术。
Hibernate好处:
1.hibernate仅仅是对jdbc技术的封装。(原因是因为jdbc技术实现比较繁琐)
2.hibernate中对sql语句进行封装。(编写程序简单,使代码和数据库解耦合,使代码变为跨数据库)
3.效率
jdbc > mybatis > Hibernate
hibernate中为了解决效率的问题:新增缓存、懒加载等技术,基于这两点效率基本和mybatis差不多了。Hibernate缺点:
1.效率还是会存在问题。(仍然没有mybatis效率高)
2.sql语句优化比较困难。
使用Hibernate:
1.导入jar包
hibernate-release-4.2.21.Final\lib\required\*.jar2.创建表和类
类:3.创建hibernate的核心配置文件
① 属性使用包装类型;
② 显示的写出默认的构造方法;
③ 实现一个可序列化接口。
默认名称: hibernate.cfg.xml
位置默认: classpath(src目录下)
1.配置JDBC链接属性 文档的3.3
<!--mysql JDBC链接配置 -->默认路径下省略hibernate.
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://127.0.0.1:3306/hibernate?useUnicode=true&characterEncoding=UTF-8</property>
<property name="connection.username">用户名</property>
<property name="connection.password">密码</property>
<!-- oracle JDBC链接信息 -->
<property name="connection.driver_class">oracle.jdbc.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@192.168.22.8:1521:ORC</property>
<property name="connection.username">用户名</property>
<property name="connection.password">密码</property>
2.配置可选配置 文档的3.4
方言 dialect 文档的3.4.1
<!-- 方言(必须有) hibernate根据使用何种数据库方言生成对应的数据库sql语句 -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="dialect">org.hibernate.dialect.OracleDialect</property>
<!--显示生成的sql语句到控制台 -->
<property name="show_sql">true</property>
<!--格式化sql语句没有该配置,sql语句会在一行显示-->
<property name="format_sql">true</property>
<!--映射文件的配置 -->
<mapping resource="com/itany/hibernate/entity/User.hbm.xml"/>
4.创建映射文件
① 默认名称: XXX.hbm.xml② 位置:和实体类同层③ ORM:对象关系映射(对象和关系型数据库映射)④ 类和表的映射
主键:属性(getter/setter)和列的映射;类和类的关系、表和表的关系。
type(java类型 也可以使用hibernate类型) 文档5.2
主健生成器: 文档5.1.4.1
//name类的全路径,table对应数据库中的表,dynamic-insert动态插入,默认为false,只为用户指定的列插入数据
//主键生成器generator,native表示自适应,如果是mysql数据库则自动变成identity,oracle数据库则自动变成sequence,并使用 seq_user序列:
如果主键为string类型则需使用:uuid或guid(guid Oracle不支持)
Hibernate api:<class name="com.itany.hibernate.entity.User" table="t_user" dynamic-insert="true">
<id name="id" column="id" type="integer">
<generator class="native">
<param name="sequence">seq_user</param>
</generator>
</id>
//类属性和表中列的映射
<!-- 1. name和column相同column可以省略不写
2. type类型无歧义的化也可省略不写,data类型有三种不可省略 -->
<property name="username" column="username" type="string"></property>
<property name="password" column="password" type="string"></property>
<property name="sex" column="sex" type="character"></property>
<property name="age" column="age" type="integer"></property>
<property name="birthday" column="birthday" type="date"></property>
</class>
Configuration 读取所有的配置文件 调用configure()方法即可读取事物是由事物管理器维护的Transaction,如果session相同,只要事物提交过,那么就算使用同一个Session获取的事物管理器也不一样。
ServiceRegistry 将读取出来的信息注册给Hibernate
SessionFactory 创建Session的工厂,创建比较复杂,极其占用资源,sessionFactory线程安全(所有的线程可以公用一个)
Session 可以理解为Conncetion(其实是一个缓存) Session线程不安全
Transaction 事物
(开启事物:session.beginTransaction();mysql:
提交事物:session.getTransaction().commit();
回滚事物:session.getTransaction().rollback();)
t_user
create table t_user()
id int primary key auto_increment,
username varchar(100),
password varchar(100),
sex varchar(1),
age int,
birthday date
oracle:
create table t_user(
id number(9) primary key,)
username varchar2(100),
password varchar2(100),
sex varchar2(1),
age number(3),
birthday date
create sequence seq_user;
单表:
新增:前二者唯一区别:保存业务的方法有两个:session.save();session.persist();session.savaOrUpdate();//根据对象的状态确定是save还是update,对象是游离态/持久态则为update;对象是临时态则是sava
save方法含有返回值(返回主键id的值)1.保存完对象就可以直接获取主键。
persist方法没有返回值
2.保存clob blob
LobHelper lobHelper = session.getLobHelper();保存map
3.动态的DML
4.hibernate中表中的列对应的是类中getter和setter方法<class dynamic-insert="" dynamic-update="">
删除:(根据主键删除)
session.delete(Object obj)修改:
delete方法里面需要跟上游离态对象。
在Hibernate中对象状态:
临时态(瞬态):内存中存在 数据库中不存在 没有被session操作过(session缓存中没有)
游离态 (托管):内存中存在 数据库中存在一条与之对应的记录 没有被session操作过(有setId则为该态)
持久态:内存中存在 数据库中存在 session操作过(session缓存中存在)
--删除态:
1. update(Object obj)
2. saveOrUpdate(obj)
3. 使用hql语句调用query的excuteUpdate();
修改持久态对象:
直接调用update方法ps:如果对象中含有clob或blob属性,需要在update之前进行刷新缓存session.refresh(user, LockOptions.UPGRADE);动态的DML update:只对修改持久态对象有效果
修改游离态对象:
查询:如果其他的属性没有赋值,update将null给更新到数据库ps:所以更新一般采用先查询再更新saveOrUpdate:根据对象的状态确定:对象为游离态、持久态-->update对象为临时态 --->save
根据主键查询:
1.查询所有的两种方法
session.get()session.load()
二者区别:
1.get方法是立即查询数据库,将数据封装到你的entity实例中.
load方式懒加载模式(代理模式),返回只是一个代理的实例,只有第一次使用的时候才会进行查询(在缓存session中查询)。2.如果查询不存在的记录:
get方法返回为null。load方法返回还是一个代理的对象,但是第一次使用抛出org.hibernate.ObjectNotFoundException
2.使用hql语句查询
query.list(); //返回list集合query.uniqueResult();//只能查一条记录
3.分页查询
查询所有记录:query.setFirstResult()query.setMaxResults()
使用HQL:
1.使用HQL语句更好的实现代码跨数据库功能.2.更好显示出面向对象。
sql语句:select * from t_user u;hql语句:from User u;(查所有select*要省略)
Query:
query.list(); //返回list集合query.uniqueResult();1.必须保证hql语句查询查来只有一条记录。2.如果有多条记录:抛出org.hibernate.NonUniqueResultException
条件查询:
HQL
select * from t_user u where u.username='aaa' and u.hobbies like %唱歌%;from User u where u.username='aaa' and u.hob like %唱歌%;
可以使用where子语句
分页查询:
query.setFirstResult()query.setMaxResults()创建hql语句--->创建Query对象(session.createQuery(hql))--->调用list()或uniqueResulet()方法.
HQL:
模拟懒加载:select * from t_user u;select u from User u;select username,password,hobbies from t_user;1.select u.username,u.password,u.hob from User u; -->返回的为Object[]2.select new User(u.username,u.password,u.hob) from User u; -->返回就是User对象 ps:User中必须含有一个与之对应的构造方法3.select new Map(u.username as username,u.password as password,u.hob as hob) from User u; -->返回的就是Map对象ps:hql语句中没有insert操作.只能执行update/delete 直接调用query.executeUpdate();select
class com.itany.hibernate.entity.User_$$_jvste00_0 extends User{
boolean isQueryDb= false;
public Integer getId(){
if(!isQueryDb){
//查询数据库
User user = session.get(User.class,Id);
this.id=user.getId();
this.username=user.getUsername();
...
this.isQueryDb=true;
}
return this.id;
}
public String getUsername(){
if(!isQueryDb){
//查询数据库
User user = session.get(User.class,Id);
this.id=user.getId();
this.username=user.getUsername();
...
this.isQueryDb=true;
}
return this.userName;
}
....
}
多对一关系 员工和部门 产品和公司...一对多关系 部门和员工 产品类型产品....
多对多关系 权限系统 选课...
一对一关系 婚姻关系
继承关系 is a
组件关系 has a
多对一:
t_product t_company
id id
name name
company_id FK
PS:外键在哪张表,哪边就是多的一方;维护关系默认给一的那方
Product Company
id id
name name
company
主配置文件可选配置:(主配置文件的信息,只要SessionFactory被创建便立即执行,底层调用SchemaExport类的create(true,true)方法)
hbm2ddl.auto (由映射文件自动创建表)建表的工具类:
none 默认,不会自动生成
create 每次运行都会新建
update 如果表存在了就不会新建了,如果表不存在就会新建
public class CreateOrUpdateTable {
public staticvoid main(String[] args) {
//获取配置信息
Configuration cfg = new Configuration();
cfg.configure();
//相当在核心配置文件中添加hbm2ddl.auto=update
//SchemaUpdate schemaUpdate = newSchemaUpdate(cfg);
//schemaUpdate.execute(true,true);
//相当在核心配置文件中添加hbm2ddl.auto=create
SchemaExport export = new SchemaExport(cfg);
export.create(true, true);
}
}
配置:<many-to-one name="类属性" column="表列,id" class="属性所在的类名"></many-to-one>
1.保存时候 按照逻辑保存(先保存一,再保存多)2.级联操作 添加cascade属性
问题:<many-to-one cascade="">
none 不级联
save-update 在保存或更新时候级联
delete 删除时候级联
all 在保存或者更新或删除都级联
delete-orphan 删除时候级联删除孤儿
查询出product后要得到company的name,此时Hibernate采用的是懒加载模式(不会立即查询companyName,使用到时再去查询,此时有可能no Session)
解决方法(两种):
(1) 在<many-to-one lazy="false">中禁用懒加载模式即lazy="false",此时对象中的关系字段会被立即加载,查询出companyName(但此时会有两条sql语句, 采用的是单表查询),另外,lazy="extra"增强,就是count技术。
(2) 要用一条sql语句解决,则采用联表查询,在<many-to-one fetch="join">(迫切左外连接),对象中的关键字段会立即查询(只对根据主键查询有效)。
抓取(迫切左外链接):
(3)如果使用hql语句查询:<many-to-one fetch="join"/>对象中关系字段会立即加载(采用的是联表查询)
配置lazy=false 采用即时加载一对多:
配置fetch只能对根据主键查询有效.如果想使用 那么就需要在HQL中添加join fetch
如:hql语句:from Product p join fetch p.company c join fetch p.apply a;(三表查询)
t_apply_o2m t_product_o2m
id id
applyNm name
product_id
Apply Product
id id
applyNm name
product Set<Apply> applys(关系多的一方使用set集合(无序,唯一)较为简单)
配置:
<set name="applys">
<key column="product_id"></key>
<one-to-many class="Apply"/>
</set>
在onetomany和manytoone的双向关系:hibernate默认维护关系交给一的一方。查询:
如果想使用多的一方维护关系 可以在<set inverse="true">将控制权反转交出(此时SQL语句没有重新update)
在onetomany和manytoone的双向关系:
查询所有会出现重复数据。
hibernate3.2之前 只能通过代码排重。
hibernate3.2版本之后 添加select distinct
多对多的关系:(通过第三张表联系起来,该表采用联合主键,两个id不一致便可入库)
Person Permission
id id
name name
Set<Permission> Set<Person> (类设计)
t_person t_permission t_person_permission (表设计)
id id personId
name name permissionId (联合主键)
**** 关系配置:(多对多关系中必须有一方要放弃维护权.invers="true")
<!-- 关系多对多 Permission.hbm.xml-->
<set name="persons"table="t_person_permission" lazy="false">
<key column="permission_id"></key>
<many-to-many class="Person" column="person_id"></many-to-many>
</set>
<!-- Person.hbm.xml -->
<set name="permissions" table="t_person_permission" inverse="true">
<key column="person_id"></key>
<many-to-many class="Permission" column="permission_id"></many-to-many>
</set>
set端 batch-size 批量查询(默认单表查询)
<property name="name">
<column name="name" sql-type="varchar(55)"></column> 指定见表时该字段类型和长度 (length="55"直接指定长度)
</property>
一对一:(基于外键和基于主键两种表设计方案,基于外键便于扩展,较常用)(1)基于外键:
t_computer_1 t_cpu_1
id id
name name
computer_id FK UK
<!-- 一对一配置 Computer.hbm.xml-->
<one-to-one name="cpu" property-ref="computer" class="CPU"></one-to-one>
<!-- 一对一 CPU.hbm.xml-->
<many-to-one name="computer" column="computer_id" class="Computer" unique="true"></many-to-one>
(2)基于主键:t_computer_2 t_cpu_2Computer.hbm.xml中: <one-to-one name="cpu" class="CPU"></one-to-one>
id id FKname name
CPU.hbm.xml中: <one-to-one name="computer" class="Computer" constrained="true"></one-to-one>
一对一类设计:
Computer CPU
id id
name name
cpu computer
组件:(人和订单中同时有地址属性使用组件方式)
t_person
id
name
city
provice
<!-- 组件关系配置 -->
<component name="address" class="Address">
<property name="city"></property>
<property name="province"></property>
</component>
继承关系:(三种设计方案,单表方式最常用)User (父类)**** (1)单表: (采用单表查询效率高,但表中始终有一个字段为空,浪费内存空间.查询时会自动添加status条件)
id
username
password
Manger (子类) Member (子类)
phone email
extends_user_1该方式只需有一个映射文件(父类,子类配置在父类中,如下:)
id
username
password
phone
status(类中不存在该属性,表中由hibernate自动加入,通常用不同数字代替member和manager)
<class name="User" table="extends_user">
<id name="id"><generator class="native"></generator></id>
<!-- 区分字段配置,类型默认为string,该配置必须在id配置后 -->
<discriminator column="status" type="string"></discriminator>
<property name="username"></property>
<property name="password"></property>
<!-- 区分字段值配置:当为Member是区分字段status值为1,属性为email -->
<subclass name="Member" discriminator-value="1">
<property name="email"></property>
</subclass>
<subclass name="Manager"discriminator-value="2">
<property name="phone"></property>
</subclass>
</class>
(2)每个子类一张表:(相当于两张单表)extends_manager_2 extends_member_2(3)父类和子类每个类一张表:
id id
username username
phone password
password email
extends_user_3
id
username
password
extends_manager_3 extends_member_3
phone email
user_id FK user_id FK
<class name="User" table="extends_user_3">(user表)
<id name="id">
<generator class="native"></generator>
</id>
<property name="username"></property>
<property name="password"></property>
<joined-subclass name="Member" table="extends_member_3">(member表)
<key column="user_id"></key> (引用父类主键)
<property name="email"></property>
</joined-subclass>
<joined-subclass name="Manager" table="extends_manager_3">(manager表)
<key column="user_id"></key> (引用父类主键)
<property name="phone"></property>
</joined-subclass>
</class>
QBC:Query By Criteria 条件查询。创建:
Criteria criteria= session.createCriteria(User.class);
//单一条件添加组合类型的条件:
criteria.add(Restrictions.eq("username", user.getUsername()));
criteria.add(Example.create(user));使用sql语句:criteria里面的方法可以模仿query对象使用.
SqlQuery sqlQuery = session.createSqlQuery(sql);转换返回值为Hibernate管理的实体:
sqlQuery.addEntity(Entity.class);转换返回值不为Hibernate管理的实体:
sqlQuery.setResultTransformer(Transformers.aliasToBean(UserVo.class));注解: 将类与表,属性与列映射关系有.xml文件移到实体类中配置. Hibernate 3.3版本前不支持
类级别:
@Entity 表示该类为Hibernate管理的实体
@Table(name="表名") 表示类映射表
对于属性配置建议在getter方法上定义:
@Id
对于主键如果为oracle:@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="aaa")
@SequenceGenerator(name="aaa", sequenceName="seq_user")
使用Hibernate自带的生成器:
@Id@GeneratedValue(generator="aaa")@GenericGenerator(name = "aaa", strategy = "native", parameters = { @Parameter(name = "sequence", value = "seq_user") })
如果是MySQL:@Id@GeneratedValue (默认主键生成器为native)
属性配置:属性与数据库列名称一致可省略不写,hibernate默认添加@Basic @Column(name="列名") 表示属性和列的对应关系
@Basic 默认给每个getter方法上添加 如果想使@Basic注解无效可以添加@Transient(忽略该getter方法注解)@Temporal(TemporalType.DATE)表示日期的类型
****多对一:注解方式只有多对一Hibernate是默认连表查询(fetch="join")
@ManyToOne //在类中配置
@JoinColumn(name="一所在方的id") //表与表的连接通过列实现
级联(@ManyToOne(cascade=CascadeType.ALL))抓取 (@ManyToOne(fetch=FetchType.EAGER)) == fetch="join"
@ManyToOne(fetch=FetchType.LAZY) == fetch="select"一对多:
@OneToMany //在类中配置,(mappedBy="放弃维护权的一方",fetch=fetchType.EAGER)**** mappedBy 和@JoinColumn, @JoinTable互斥(不能同时存在)
@JoinColumn(name = "一的一方id")
多对多:(需要有一方放弃维护权)
@ManyToMany(fetch=FetchType.EAGER)一对一:
@JoinTable(name = "t_person_permission",
joinColumns = { @JoinColumn(name="permission_id") },
inverseJoinColumns={@JoinColumn(name="person_id")})
*****使用外键方式:
一面注解如下:
@PrimaryKeyJoinColumn (采用主键连接,不生成外键)@OneToOne
另一面注解:
@ManyToOne使用主键方式,使用注解不可添加外键。
@JoinColumn(name="computer_id",unique=true)
组件:
类组件类添加
@Embeddable(表示该类是一个可嵌入的类)属性覆盖(同时在两张表中引用属性,使列名称显示不一致需用属性覆盖)
@Embedded 表示嵌入属性
@AttributeOverrides(value = { @AttributeOverride(name = "city", column = @Column(name="orderCity")) ,继承关系:(父类中需配置:类、表、继承策略、区分字段)
@AttributeOverride(name = "province", column = @Column(name="orderProvince"))})
(1)单表方式:
@Inheritance(strategy=Inheritance.SINGLE_TABLE) 配置继承策略(2)每个子类一张表方式:
@DiscriminatorColumn(name="区分字段名") 配置区分字段,在父类中配置
@DiscriminatorValue(Value="2") 区分字段的值,在子类中配置
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) 配置继承策略(3)每个类一张表:
@MappedSuperclass 映射父类 (此两项均在父类中配置)
@Inheritance(strategy=InheritanceType.JOINED) 配置继承策略
该方式子类在父类中引用的id默认名为id若要修改则在子类中配置:@PrimaryKeyJoinColumn(name="user_id")数据源:
1. 添加jar包
hibernate-release-4.2.21.Final\lib\optional\c3p0\*.jar2. 在核心配置文件中添加
二级缓存:<!-- 数据源配置 -->
<property name="c3p0.max_size">5</property>
<property name="c3p0.min_size">2</property>
<property name="c3p0.timeout">5000</property>
一级缓存:(不可以关闭,只要使用Hibernate就存在)
Session级别二级缓存:(可以关闭,可以打开) 默认关闭
session.clear(); 清除所有缓存数据
session.evict(user); 清除缓存中的user对象
SessionFactory级别1.不会经常发生变化的数据.
可以放入缓存的对象:
2.比较不重要的数据。
1.导入jar包hibernate-release-4.2.21.Final\lib\optional\ehcache\*.jar
2.配置文件hibernate.cfg.xml
3.配置ehcache缓存配置文件.<!-- 二级缓存 -->
<property name="cache.use_second_level_cache">true</property>
<!-- 缓存提供者 -->
<property name="cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
maxElementsInMemory 缓存的最大记录数(0 没有限制)4.配置需要放入缓存的对象.
eternal 表示缓存的对象是否永远存活
timeToIdleSeconds 表示空闲清除时长(秒)
timeToLiveSeconds 表示最大存活时长(秒)
overflowToDisk 缓存满了是否溢出到硬盘
maxElementsOnDisk 溢出到硬盘最大多少记录数
memoryStoreEvictionPolicy 缓存硬盘全满了对象时长还没有达到,配置移除策略
FIFO 先进先出
LRU 最远使用删除 (默认)
LFU 最少使用频率删除
三种配置方式:
1.在核心配置文件hibernate.cfg.xml中配置:
<class-cache usage="read-write" class="com.itany.hibernate.entity.User"/>
2.可以在映射文件中配置:
在class标签中<cache usage="read-write"/>
3.还可以在注解中配置:
在要放入缓存的类上添加 @Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
注意:clob、blob不可以使用二级缓存。