Hibernate学习笔记

关于bean中属性作用域的问题。(隐藏 某些属性,同时希望能够通过hibernate抽上来)
有时候,我们不希望能够让某些属性暴露在外面,比方说书中的例子类Category。
public class Category {   
private String name;
private Category parenteCategory;
private Set<Category> childCategories = new HashSet<Category>();
private Set<Item> items = new HashSet<Item>();
public void addChildCategory(Category childCategory){
if(childCategory==null){
throw new IllegalArgumentException("Null child category!");
}
if(childCategory.getParenteCategory()!=null){
//如果原父目录不为空,则在原副目录中删去子目录,
childCategory.getParenteCategory()
.getChildCategories()
.remove(childCategory);
}
childCategory.setParenteCategory(this);
this.childCategories.add(childCategory);
}
}


其不希望存放子目录的childCategories直接暴露给外面。就可以用把其get,set方法设为私有,或者删除。书上说可以直接的字段访问进行持久化,这一点我还是看不太明白。估计以后会说吧。
在这里他绝少一个不错的方法,返回只读的set和map。这个方法是Collections中的unmodifiableXXX,很不错的一个方法。

hibernate中的脏检查
对于在hibernate中,对于某一个对象的属性的改变,是判断它的值,而不是统一性。所以获取方法的时候,返回一个不同的对象比返回由hibernate传递到设置方法的对象来的安全。
比方说下面的方法(返回User类的firstname属性)就比较安全。
public String getFirstname(){   
return new String(firstname);
}

这里我的理解是,如果要返回从hibernate抽上来的对象。那么如果这个返回对象改变。那么hibernate可能会自动的进行更新。比方说说这里的firstname。我返回出去之后,那么如果我改变这个值,hibernate也会更新。
但是对于集合,则只是判断其统一性,而不是值的改变。
比如说下面的代码需要尽量的避免。

public void setNames(List namesList){   
names=(String[])namesList.toArray();
}

public List getNames(){
return Arrays.asList(names)
}

因为每次都会产生不同的对象,而使得统一性改变。

动态SQL语句生成。(SQL语句不显示所有的数据表的字段)
就是class标签中的dynamic-insert和dynamic-update语句。这个时候生成的sql语句,只会出现需要更新的字段。(估计这东西到时候看看api就能了解个大概。只是会看不懂。)


使查询返回的实体(类)不可变。
class标签中的mutable属性。设置成false
这个属性个人觉得有点比较大的限制。毕竟一旦设置,那么一切也就没了。这个就有点太严谨了一点。

给查询实体命名。(重命名类的实体)
标签是:

<hibernate-mapping>  
<import class="auction.model.Auditable" rename="IAuditable"/>
</hibernate-mapping>

以后的HQL查询的时候,直接输入IAuditable就好了。不过这个觉得比较奇怪,因为import标签没有说明给。

实现命名约定
比方说一个类User,而规约约定的是数据库表的前缀是db。那么数据库中对应的表应该是db_user。那么用了这个就可以在配置文件中直接写对应的表为user。
具体的用法是写一个类,继承ImprovedNamingStrategy或者实现NamingStrategy接口。然后调用Configuration中setNamingStrategy方法
这个方法觉得有点鸡肋。唯一的用处就是相同的对象要进入不同的表中的时候用到。但是用这个方法感觉很原始,首先,需要不创建不同的SessionFactory。其次,用继承也能起到差不多的效果,而且继承更加的方便。唯一的用处就是写映射文件的时候,方便一点了吧。

属性访问,衍生属性(计算值),默认属性等
看具体api吧,比较清楚一点。

映射组建
使用的是Component标签来进行。我发现如果要使用到parent子标签时,组件类当中要有一个属性,指向包含它的类。
继承映射
继承映射书上介绍了4种方法。其实真正意义上的只有3种。因为第一种方法完全就是每个子类建一个不相关的映射文件。这样做对于多态来说,毫无意义。而且和基本的映射没有区别,这里也就不再复述了。
第一种方法的标签为union-subclass。中文的翻译很拗口,叫做每个带有联合的具体类一张表。我习惯称之为union。因为Hibernate内部,各个子类的关系就是一种联合的关系。
这种方法,每个具体的子类代表一张相应的表。各个表之间在数据库那层更本没有关系。只是在Hibernate内部,知道每张表代表的是不同子类的对应的不同的表。所以,在进行多态查询的时候,只通过union把各个表联合成一张大表,做为查询的表,进行查询的。
也就是因此,如果采用这种方法,不太适合那种需要经常进行多态查询的表。因为每次多态查询都需要把所有子类合并成一张大表。在这里书上有点错误。父类的id的生成器的生成策略是native。至少在mysql的环境下是不行的。其他服务器估计也会产生问题。因为native能保证一张表中的主键的连续。但是无法保证在多个表中主键的唯一。所以无法在这种,会生成多张表的,且需要多张表的主键都唯一,这显然不行。
第二种方法是得标签是subclass。叫做每个层次一张表。具体的说,就是所有单个类的数据都放在一张表上面。
打个比方来说,比方说主类A有a,b两个属性,子类B其特有c属性,C则有d属性,那么代表这个的表中,就会有a,b,c,d 4列。如果这样设计的优势就是查询不再需要多张表连接了。速度会快很多。同样的带来的问题就是,如果子类有很多,同时每个子类都有着其独特的属性,那么这个表就会很大,同时很多列会浪费掉。还有一个很头痛的问题就是子类的独有属性不能设置成unique。
所以这种办法,书上建议是最好的子类之间的区别是其行为,而不是属性的时候使用。这样生成的表就会精简很多。
第三种方法,标签是join-subclass。其数据库实现是主类的属性一张表,然后各个子类的信息一张表。且子表的主键参照记录在主表中记录的主键。
我觉得这种方法感觉很中性,因为第一种方法如果不考虑多态查询的话是很好的。而第二种呢,多态查询时方便。但是如果每个属性都有着自己的独特属性,就会一些数据库层次的东西。而这种方法则是一种平衡。
最后混合方法。其实就是一种嵌套。不过记住。没有subclass。如果用了subclass。则是子类的子类了。
Hibernate数据类型
这一些基本看书吧。基本上技术上那点东西。不过我就觉得要注意两点
其一,不同的string对应的值。其二,和Hbiernate无关,就是Calendar的月份起始是0。

(附件为自己根据书上写的例子,自定义类型由于书上例子写的很模糊,后来去网上找了一下原本的案例。看的一般懂,所以没有对例子中的代码改进)

集合映射
bag:这是一个java中没有的东西,如果概念上简单的来说,就是一个可以重复的set。书中在介绍的时候,基本就介绍了一个<idbag>标签来实现bag的。后来查了一下资料,知道还有一个<bag>标签。两者的却别就是前者有一个id的列。
无论对应的是哪一个。都是对应着Collection类
至少我觉得,<idbag>要比单单的<bag>要好很多。bag的定义是可以重复,无序的一个集合。听起来不错,但是在大多数情况下,对于数据一个重要的要求就是能够被区分。而一旦用了<bag>标签,存入数据库的时候,如果值相等,那么就没有了一点区别。
如果用<idbag>标签,那么就需要一个<collection-id>其中的generator不能使用native或者identity。我查了一下api。3.2不支持identity,而3.3的直接写不支持native。我觉得可能是不支持identity。因为选了native就是自动的在identity和sequence之间根据数据库进行选择。
List:基本上没什么好说的。就是标签要熟悉一点。其区分记录用的是<list-index>标签
map:<map-key>标签。map是一个对数据的存储。一个key。对应的就是<map-key>
set:觉得没什么好说的了。
其实从总体上来说,所有的集合标签都是相当死。没什么可以深究的。所有的代码都是那个样子。关键在于熟记和理解集合的概念。

映射父/子关系
不知道官方说法是怎么说的。反正我的觉得这个名字有点变扭。感觉和继承关系有点重名。具体的实现其实就是在集合的标签中加入了一些<one-to-many>之类的标签。从数据库的表的层面上来说没有任何实质上的变化。
但是从逻辑上来说,这种映射和集合的映射有着本质上的区别。集合的映射属于一种整体性的映射。而父子的映射属于对象与对象的映射。对于此,我也是在朦胧中摸索,也无法讲的很明白。

inverse
inverse表示的是告诉Hibernate哪边不用维护。就以书中的例子来说(具体可以看我附件中的例子,大体上是Item和Bid两个类)。在inverse为false的时候,Hibernate会发出5句语句。一句在Item表中加入一条语句,然后两句在Bid类中加入两条记录的两句语句。最后对更新刚才两条数据的ITEM_ID选项(就是和Item表关联的字段)。
因为在hibernate内部,每一个类中的字段都是被“监视”着的,默认情况下一有风吹草动就会更新。而在java中,双向关联需要两个的是在两个对象中设置不同的两个属性互相指向。但是在数据中,这种联系往往是一个外键就解决了问题。造成的结果就是一个数据库中的一列,被两个字段所所引用。所以当改变的时候,java中需要改变两个对象的相关属性,而数据库中则只需要改变一个就可以解决问题了,java的改变要比数据库多一次。hebiernate监视的java代码,而不是数据库,这也就是产生多余sql语句的原因。
而inverse就是告诉Hibernate,这里只是一个镜像,不用维护。从而达到了减少sql语句的作用。
不过在写inverse例子的时候,我发觉了对于值类型的类,一定要写好equals和hashcode方法,并且这些方法最好要用和业务逻辑相关的值。不然可能hibernate很容易会产生不必要的更新操作。

级联
这是很容易和inverse相混淆的一个概念。因为他们的区别很难用言语表达。我现在理解是inverse告诉hibernate什么不该做。而级联告诉的则是什么应该做。
级联分为传播,删除和孤儿。分别对应的是数据的更新(包括插入),父对象的删除和子对象从父对象中被删除来确定。

问题
其实书中的bids的集合是对外开放的。set和get方法都是public。当然如果在实际操作中都会知道这是很不保险的做法。我也就自己想着解决办法。
首先我想到了就是删除set方法。结果失败。然后就把代码改成了这个样子。

public Set<CPBid> getBids() {     
return Collections.unmodifiableSet(bids);
}

SuppressWarnings("unused")
private void setBids(Set<CPBid> bids) {
this.bids = bids;
}


倒是运行过了。此时如果把setbids设成public。在读取item内部的bid时,则会报一个很奇怪的错误。org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: collection.parent.CPItem.bids。觉得很是奇怪。
首先我代码中并没有任何的删除语句。后来想想,可能是因为hibernate获取bids的时候也是调用这个get方法,而这方法的返回值肯定与原来其内部存储的不一样(因为是新建的)。而此时的bid指向的是原来那个的缘故吧?
但为什么把set设置成private就能跑过呢?很奇怪啊
心得:
一,hibernate关心的永远只是一个一个属性,而不是数据库当中的表。这也就是为什么inverse属性不放在多一方Item的时候,数据库更新的是Bid所代表的那张表。因为一旦建立了这种联系关系。Item和Bid有联系,在数据库当中,都是维护着Bid当中关于Item的外键。
但是数据库中的表和OO有着一些格格不入。因为OO对象与对象之间的关系是指针。比如书中的例子,一个Item,指向n个Bid。在java的世界里。有多少个Bag和Item。就会产生多少个对象。他们直接互相的指针标示着他们的关系。
但是在数据库中,则是完全另一幅景象。数据库中,对象与对象的关系通过的是通过外键来形成的。代表一个对象的一条记录有着另一个对象的记录的时候,就表示两个对象之间建立了联系。
Hibernate在处理这种关系的时候,往往会把这表示联系的关系外键放在一得一方。one-to-one是联合主键。而many-to-one则是把外键放在了one的一方。这点起初让我很混淆。因为方向也好,真的让我很糊涂。

双向和单向
这个概念可能有点困扰。因为在hibernate中,只要在类当中有着链接。那么一定一个找到另一个。比如说例子中的bid和item,item中的bid是map或者set存放,那么通过item,一定能够找到相应的bid。只是单向的导航只是无法确定或者说必须先获得整体的包含bid的map,然后通过代码进行查找特定的bid。而双向的可以直接找到相应的bid。
所以来说,如果说需要双向导航,在表示两者这件一对多或者多对一的关系的时候,只能使用<idbag>,<list>和<map>这样的标签。因为这些标签使得在多端,会产生一个和一端相关,和多端无关的标识。而set,bag这种标签,则不会有这种东西

<mang-to-one>之类的标签
开始的时候,我对于这些标签和迷惑。不知道其到底有着什么特别的作用。
慢慢的,明白了这只是一个代表着一个类中的属性。和property之类的标签有着一样作用的。表示这个属性对应着何种的值。只是有了这个标签,就告诉了Hibernate。这个属性和其他的属性不一样。不能够单独存在。其和其他类有着特殊的关系。

<many>端的unique
首先,从hibernate来说,其处理的是OR映射框架。而在操作层上面,其关心的只有在类当中的属性。在hibernate内部,只有属性和表中的关系应该是多对多的关系。但是从属性一端来理解hibernate的映射,将会是比较方便的方法。
所以来说,如果many一端设置了unique,就等于把这一端设定成了one端。因为属性对应的字段为唯一,那么一切就好办很多。

<many-to-many>标签
其实这个标签和上面的标签没什么本质上很大的区别。也是一种映射的表示。
至于中间件的处理,如果简单点,就是一个类的映射。当然复杂一点,就要深入讨论了。这里就不展开了。

继承
这里的继承的与前面不同的是外部调用多态的实现。很简单,就好像组件的组装了。
当然有一点复杂,但是书上也说了。这个复杂的东西被用的概率很少很少。所以我就知道,解决这些问题是通过一个叫做<any>的标签。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值