Support Wikipedia Follow My Heart

2012年8月2日星期四

Object Relational Mapping 学习笔记(1)

好久没写博客了,今天就写一写吧。这两天在研究jpa 2.0规范,做一下笔记,一方面加深印象,一方面也方便自己以后复习。

在公司做project的时候,看到有人是这样用jpa的,唉,完全没有体现jpa的价值所在啊。想来jpa技术虽然不难,但是也还是有一点门槛的,所以可能很多同学都会图方便(不用专门学习,不用手工写mapping),而采取这种方案。

jpa2

 

这个方案简单来说,就是通过额外编码(MyDTO, MyDTOFactory),事先将java 对象之间的关系解构,生成仅仅包含java 基本类的Entity对象。持久化时存储到数据库中的,也就仅仅是这些个简单的Entity对象而已。这种方式由于Entity中仅仅包含基本类,所以持久化时不需要添加特殊的jpa标签进行定制,非常容易上手,所以在公司内部的project中很有“人气”。 但是这种方案一方面会造成代码冗余,另外,基本没有体现出JPA技术的真正用处。

使用JPA是为了方便,为了不用手工将java object解构,为了使得代码更简明直观易于维护!因此,我们的目标是这样的:

jpa

 

Object千变万化,所以要实现上面的目标,不手工定制持久化方案是不行的。但是持久化的手工配置并不是太可怕的东西,一会儿就能学懂。

首先明确一点,Object中的基本类型数据的持久化是很简单的,不需要特别的标签(除非要设置fetch type)。 Object中的复杂类型数据(其他类,基本数据的集合,其他类的集合)等等(不妨称其为“子对象”),需要根据其“特点”和“与父对象的关系”,进行针对性的配置。

1, “特点”的分类

子对象的“特点”,这里指的是它的独立性。可以分为两类,即:

  1. 子对象自身包含区别于其他同类子对象的主键。因此在数据库中,它可以单独存储与其他数据表中,通过主键的链接,实现从父对象到子对象,或者子对象到父对象的查找操作。下文称这类子对象为“独立子对象”。E.g., Employee对象中有一个Department子对象,指向所述的部门对象。每个部门都有自己的编号。 demo
  2. 子对象自身没有主键,仅仅是父对象一部分状态数据的集合体,如果不嫌麻烦,拆开来一条一条写出来也一样的效果。在数据库中的体现就是子对象不是单独的数据表,而是“嵌入(Embedded)”到父对象的表中。下文称其为“嵌入子对象”。比如Employee对象中,需要记录本人的家庭住址,而住址包括邮编,街道,市区等等信息。这些信息统合为Address子对象,并没有独立的标识,而是嵌入到某一个具体的Employee对象中。

         本文主要介绍独立子对象的持久化配置方法。

 

2, “关系”的分类

     对于独立自对象,其与父对象的关系,无外乎以下四类,即:

No. 对应父对象数量 对应子对象数量
1 Many One
2 One One
3 One Many
4 Many Many

 

3,“Many-to-One”关系如何持久化

   这可能是最常见的一组关系,比如刚才举的Employee(父对象)和Department(子对象)的关系。该关系的持久化设置只需要针对父对象的java class就行,子对象的class不用碰。当然了,子对象持久化需要的基本配置(比如@Entity, @Id 等标签)还是需要的。

   最简单的例子:

Employee Class


  这个例子的实质,是在Employee对应的数据表中,添加一列,用于存储对应的Department的Key。 列名是默认方法生成的(如果Department表中的主键列名是ID的话,那么上例对应的列名就是“Department_ID”)。 如果您想自定义列名(比如改为“CustomeName”),或者您必须自定义列名(比如DB Schema已经有了,不能改),那么请参考下面的例子。


   稍微复杂一点的例子:


Employee Class

上面的例子体现的关系,都是从Employee到Department的单向ManyToOne关系。如果在Department中加入了List<Employee> employees 这样的属性,就使得Department对象具有了从Department到Employee的OneToMany关系(这时候Deparment变成父对象,Employee就变成子对象了)。在这种情况下,如果仅仅配置了Employee对象的ManyToOne关系是不够的,还必须如后文所述,对Department的OneToMany关系也进行特别配置。


4,“One-to-One”关系如何持久化


    比如一位员工需要有一个独立的衣橱。每一个独立的衣橱都对应一位员工。所以员工和衣橱的关系就是OneToOne关系。


    OneToOne关系的配置和ManyToOne关系的配置很类似,只要修改标签的名字就可以了,比如下面这个简单的例子。


Employee Class
Wardrobe Class

   如果您想修改JPA自动生成的外键列列名,那么更上文所述方法一样,在Employee类的myWardrobe属性前,加入@JoinColumn标签进行配置。


   如果这时候Wardrobe类中也加入了Employee类的引用,那么就使得Wardrobe类也具有了从Wardrobe到Employee类的OneToOne关系。所以持久化时也需要特别配置。 Employee对象的配置方法不变,Wardrobe对象如下所述进行配置。


Wardrobe Class

  这里的配置,就是加入了“mappedBy”属性,告诉JPA,这个OneToOne关系,和Employee类里为“myWardrobe” 属性配置的OneToOne关系是一对的,修改的时候一定要连动更新。


5,“One-to-Many”关系如何持久化


   这是另一种非常常用的关系。其内容与ManyToOne关系正好相反。对于ManyToOne关系,JPA会自动生成外键匹配列。然而,对于OneToMany关系来说,情况就复杂了。简单来说,在一个关系数据表中,仅仅一行数据,是很难在某一列中存储任意多个值的(除非把事先通过编码把这些值合成一个(比如序列化),以后使用时也额外编码来实现子对象的索引,但是这就不是JPA的方式了。)。所以,JPA的做法,是要求用户将OneToMany关系,转化为与其对应的ManyToOne关系,强制在子对象中加入指向父对象的引用。


   复杂么?其实不复杂,请看下面的例子。


   JPA无法完成持久化的初始代码:


Failed Example of One-To-Many

   JPA可以接受的等价方式:


OK Example of One-To-Many


    如果您想修改JPA自动生成的外键列列名,那么更上文所述方法一样,在Employee类的myDept属性前加入@JoinColumn标签进行配置。另外,


 


OK Example of One-To-Many

 


6,“Many-to-Many”关系如何持久化


乍一看这个关系可能不好理解,其实现实生活中还是挺常见的,比如Employee和Project的关系。一个Project有很多成员,一位员工可能同时属于多个项目组。这个关系的配置给OneToMany关系很像,比如要父子对象都拥有指向对方的引用。换句话说,他们之间的关系是平等的,都是ManyToMany,也就是说,我们认为哪一个关系是父对象都没关系。


这个关系的配置方法是,任选一个对象作为子对象;对于子对象必须声明“mappedBy”属性,其他一样。


Failed Example of Many-To-Many

OK Example of Many-To-Many 1
OK Example of Many-To-Many 2

ManyToMany关系在JPA中处理时,其实是在后台建立了一个JoinTable,用来存储父子对象主键的组合关系。如果您需要自定义这个JoinTable的表名或着列名,那么可以在ManyToMany关系的父对象(那个都OK,选一个)定义。


Custome Join Table


7,其他本文中没有讲到的东西


JPA中最简单的基本数据类型的持久化  ----  这个大家随便google一下就明白了。


嵌入式对象的持久化问题 ----  这个下一篇学习笔记里面写。

















高级mapping,比如List和Map的持久化问题 --- 这个以后再写。。。。

2012年3月19日星期一

XPath 遇到 命名空间(Namespace)

今天在一个拥有命名空间设定的xml文件上用xpath查节点,发现怎么写xpath语句都查不到对应的东西,总是返回null.仔细google一下,发现原来是命名空间的问题。

对于一个没有命名空间的xml文件,比如:

no namespace

<?xml version="1.0"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>project</artifactId>
    <name>example</name>
    <url>http://maven.apache.org</url>
    <version>1.1.0</version>
</project>


如下的xpath语句可以顺利执行,并得到预想的结果。


no namespace xpath


root.selectSingleNode("//version") // 获得version 节点

然而,加入命名空间后,这两个xpath的返回值都变成null。


namespace xml

仔细分析,发现新的xml文件中,有两组命名空间,分别是前缀为 ”xsi”的空间(url=http://www.w3.org/2001/XMLSchema-instance)和无前缀的默认空间(url=http://maven.apache.org/POM/4.0.0)。 因此,当调用xpath语句时,候选节点自动根据命名空间进行了过滤,即:









当xpath中未指定命名空间,如”//version”搜索范围是所有不属于任何命名空间的节点。但是在该例中由于存在默认命名空间,所以这里什么都找不到。
当xpath中指定命名空间前缀时,如”xsi”搜索范围是所有隶属于xsi命名空间的节点,也就是以xsi: 开头的节点及其子节点。


因此,为了顺利查找得到隶属于默认无前缀命名空间的version节点,我们需要:



  1. 将默认无前缀命名空间改为有前缀的命名空间。

  2. 将新的前缀名加入到查找所用到的xpath语句中。

代码如下:


java

2012年3月9日星期五

Eclipse 远程调试 java程序

今天一个偶然的机会,研究了一下用eclipse调试远程java程序。

具体操作如下:

Step1: 配置远程被调试的java程序的启动参数(JVM参数),打开远程debug监听端口。

Step2: 启动远程java程序.(注意,配置完参数后,一定要先把这个远程java程序run起来。如果是在eclipse中启动,要用run方式,不是debug方式。)所要用到的参数如下:

VM Parameter


Step3: 在本地的eclispe中创建用于远程调试的debug profile。具体过程如下:

 

step1 

Step2

step3

step4

 

Then you can debug your code remotely as follows.

Note: the remote Java Code will be suspended as soon as it is started (‘cause you set “suspend=y” in VM arguments). When you start “debug” in your local Eclipse, the remove JVM will resume running until meet the first Breakpoint.

注意端口,地址,编码,代码,JRE环境等等因素的匹配问题。

 

final

Wish you enjoy it!

2012年2月28日星期二

CubicTest Version Up! (2.0.3 --> 2.0.5)

I folked the original CubicTest (in GitHub),
update libraries, fix bug, and increase the version from 2.0.3 -> 2.0.5

Now the source code is available at : https://github.com/cnkmym/cubictest

2012年2月10日星期五

Keys’ Char Code List

These code represent the keys. They are very useful in Web Testing.

Char Code Key Name
8 Backspace
9 Tab
13 Enter
16 Shift
17 Ctrl
18 Alt
19 Pause
20 Caps Lock
27 Esc
33 Page Up
34 Page Down
35 End
36 Home
37 (Arrow) Left
38 (Arrow) Up
39 (Arrow) Right
40 (Arrow) Down
45 Insert
46 Delete
48 ~ 57 (Numeric) 0 ~ 9
65 ~ 90 (Alphabetic) A ~ Z
91 Left Windows
92 Right Windows
93 Context Menu
96 ~ 105 (NumPad) 0~ 9
106 (NumPad) *
107 (NumPad) +
109 (NumPad) -
110 (NumPad) .
111 (NumPad) /
112 ~ 123 F1 ~ F12
144 Num Lock
145 Scroll Lock
186 ;
187 =
188 ,
189 -
190 .
191 /
192 `
219 [
220 \
221 ]
222

2012年2月8日星期三

Making Your Tech Conference Presentation, and Experience, Not Suck

I found this excellent post about improving the quality of technical presentation.
Original Link : http://www.softwarequalityconnection.com/2011/11/making-your-tech-conference-presentation-and-experience-not-suck/

中文翻译如下: (翻译原文链接:http://www.williamlong.info/archives/2989.html)


技术演讲的技巧和经验


导读:如果你在编程大会上发表演讲,不论是开放式的BarCamp或是像OSCON那样精心组织的大型会议,你只有很短的一段时间将脑中的信息传达给听众,所以请仔细把握。
技术会议的成本非常昂贵,不仅仅是经济上的成本。即使像BarCamps这样的免费会议也需要与会者投入宝贵的时间。大家放下手头的工作或者割舍与家人一起的时光,而这段时间是无法用金钱衡量的。大会(按照主题)浓缩成45分钟的专题,演讲者和听众都因该充分利用这段时间。
开场白通常都是浪费时间,演讲一开始通常都是无用的信息。如果你是演讲者(我会告诉你):我不在乎你为哪个公司工作,除非这与你要演讲的主题有关;我也不需要了解你正在讨论的产品历史。如果你的演讲一共45分钟,那么花5分钟时间介绍自己就意味者浪费了11%的时间在介绍没有人会关心的信息。
作为听众,在演讲一开始我就想知道能否从中得到一些有用的东西,并确定是否需要收拾东西转向另一个会场。
下面是一位注重实效演讲者的开场白:“嗨!我叫 Sharon Bosworth,感谢参加今天的演讲。今天我要讲的主题是如何使用FooTest测试框架测试Perl程序。一年半以前我加入了 Amalgamated Widgets,我们有一个上千行的代码库,项目经常由于bug延期。现在,我所在的4人团队已经成功地将项目周期缩短到之前的一半,管理团队爱死我们了。接下来我要介绍我们是如何使用 FooTest达成这一目标的,与此同时还会介绍如何在你的公司中采用这个框架。”
在这个开场白中,演讲者介绍了她将要讨论的内容、为什么有趣以及听众能从中得到哪些收获。请留意Sharon没有介绍的内容。她没有介绍她住在哪里,为多少家创业公司工作过,或者参与的开源项目有哪些。她只向听众传达了演讲本身必要的信息,没有任何废话,并成功地抓住了听众的注意力。
作为一名听众,一般在开场几分钟内你就能辨别演讲者是否优秀,以及即将进行的演讲是否有价值。如果答案是否定的,请马上起身去别的会场。通常我会在日程安排中选择一个备份,如果我的第一选择失误,就可以马上去听那一场。不要在演讲一开始就上网、检查邮件、Twitter更新或者FarmVille收获如何。如果你浪费了开场的5分钟,请好好把握剩下的40分钟不要做其他的事情。演讲的时间是宝贵的。
译注: FarmVille是Facebook上的游戏,由美国的社交游戏开发商Zynga开发,类似于国内的开心农场。
技术演讲那些事:如何不让你的演讲令人讨厌
Farmville是一个很好的测试。它无须动脑,是人们在感到无聊时用来打发时间的游戏。如果你在演讲的内容还不如种虚拟土豆有意思,那么你的演讲肯定有问题。
如果在你演讲切换到下一个主题时看到有人在玩FarmVille。当然,不可能每位听众都会全神贯注,但是如果大多数人都是如此,即使你花费很多时间写了幻灯片并准备了示例,这时你还是应当夺回他们的注意力。也许你讲的内容与听众并不十分相关,因为他们没有使用最新的C++或者你所说的移动测试技术并不能在他们的平台上使用。除非你看到相当一部分的听众专注地看着你,这时你才可以说“这里只是简单的介绍,如果你希望了解更多请会下联系我”然后再切换到下一个主题。
永远不要把演讲变得乏味。我的导师之一,卓越演说家Mark Jason Dominus曾经说过:“如果要在娱乐性和知识性之间选择,作为演讲者你应该选择前者。人们会愿意把生命中45分钟交给你”。
务必让演讲变得有趣。即使演讲内容与听众需求无关,你也可以让演讲变得有趣。虽然我不想通篇都谈论FarmVille,但是CodeConf大会上的一场精彩演讲讨论的就是Zynga公司的基础架构,通过它FarmVille得以支持上百万用户。演讲者讨论了如何处理移动设备的延迟问题,以及Farmville后台进行异步通讯的同时,如何让用户在操作时仍然感觉程序在即时相应。这让我想到自己应用程序中的延迟问题。最重要的是,这个案例研究非常吸引人。
最优秀的演讲者不会让听众空手而归。如果可能的话,可以提供3到5个回到办公室就能做的练习。如果打印在纸上那就更好了。还有一个附加的好处是,听众不用疯狂的做笔记而可以专心听你讲解。
最后一个建议是,在让演讲变得有趣的同时一定要注意:使用幽默一定要小心。技术演讲中带有男权主义、性联想以及其他不专业内容的言论都会招来全场听众的愤怒并让他们觉得恶心。一段对女性“性感”打分的示例代码可能让你觉得有趣,但是起码会让听众分心甚至让他们觉得被冒犯。作为听众,如果你在某个演讲中发现不专业的内容,请不要害怕马上起身离开。即使你没有这么做,也可以让演讲的组织者知道这个问题。
你参加技术会议中见到的最好的和最糟糕的事情是什么?请在评论中与大家分享。也许这样我们就可以阻止一位,仅仅一位演讲者犯同样的错误从而浪费你宝贵的时间。

2012年2月5日星期日

JVM内部的内存管理概述

在CSDN网上看到一篇不错的关于JVM内存管理的概述,现转载如下。原文地址是http://topic.csdn.net/u/20090602/10/bd9d4ba2-dbeb-49b3-8651-67c5de2b228a.html


补充一句:
内存测试工具VisualVM已经随着jdk同时免费发布。
用起来很不错。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
近期看了看Java内存泄露的一些案例,跟原来的几个哥们讨论了一下,深入研究发现JVM里面还是有不少以前不知道的细节,这里稍微剖析一下。先看一看JVM的内部结构——


如图所示,JVM主要包括两个子系统和两个组件。两个子系统分别是Class loader子系统和Execution engine(执行引擎) 子系统;两个组件分别是Runtime data area (运行时数据区域)组件和Native interface(本地接口)组件。
 
Class loader子系统的作用:根据给定的全限定名类名(如 java.lang.Object)来装载class文件的内容到 Runtime data area中的method area(方法区域)。Java程序员可以extends java.lang.ClassLoader类来写自己的Class loader。  

Execution engine子系统的作用:执行classes中的指令。任何JVM specification实现(JDK)的核心都是Execution engine,不同的JDK例如Sun 的JDK 和IBM的JDK好坏主要就取决于他们各自实现的Execution engine的好坏。

Native interface组件:与native libraries交互,是其它编程语言交互的接口。当调用native方法的时候,就进入了一个全新的并且不再受虚拟机限制的世界,所以也很容易出现JVM无法控制的native heap OutOfMemory。

Runtime Data Area组件:这就是我们常说的JVM的内存了。它主要分为五个部分——
1、Heap (堆):一个Java虚拟实例中只存在一个堆空间
2、Method Area(方法区域):被装载的class的信息存储在Method area的内存中。当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,然后读入这个class文件内容并把它传输到虚拟机中。
3、Java Stack(java的栈):虚拟机只会直接对Java stack执行两种操作:以帧为单位的压栈或出栈
4、Program Counter(程序计数器):每一个线程都有它自己的PC寄存器,也是该线程启动时创建的。PC寄存器的内容总是指向下一条将被执行指令的饿地址,这里的地址可以是一个本地指针,也可以是在方法区中相对应于该方法起始指令的偏移量。  
5、Native method stack(本地方法栈):保存native方法进入区域的地址

以上五部分只有Heap 和Method Area是被所有线程的共享使用的;而Java stack, Program counter 和Native method stack是以线程为粒度的,每个线程独自拥有自己的部分。

了解JVM的系统结构,再来看看JVM内存回收问题了——
Sun的JVM Generational Collecting(垃圾回收)原理是这样的:把对象分为年青代(Young)、年老代(Tenured)、持久代(Perm),对不同生命周期的对象使用不同的算法。(基于对对象生命周期分析)


如上图所示,为Java堆中的各代分布。  
1. Young(年轻代)
年轻代分三个区。一个Eden区,两个Survivor区。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制年老区(Tenured。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。  
2. Tenured(年老代)
年老代存放从年轻代存活的对象。一般来说年老代存放的都是生命期较长的对象。  
3. Perm(持久代)
用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=进行设置。

举个例子:当在程序中生成对象时,正常对象会在年轻代中分配空间,如果是过大的对象也可能会直接在年老代生成(据观测在运行某程序时候每次会生成一个十兆的空间用收发消息,这部分内存就会直接在年老代分配)。年轻代在空间被分配完的时候就会发起内存回收,大部分内存会被回收,一部分幸存的内存会被拷贝至Survivor的from区,经过多次回收以后如果from区内存也分配完毕,就会也发生内存回收然后将剩余的对象拷贝至to区。等到to区也满的时候,就会再次发生内存回收然后把幸存的对象拷贝至年老区。

通常我们说的JVM内存回收总是在指堆内存回收,确实只有堆中的内容是动态申请分配的,所以以上对象的年轻代和年老代都是指的JVM的Heap空间,而持久代则是之前提到的Method Area,不属于Heap。

了解完这些之后,以下的转载一热衷于钻研技术的哥们Richen Wang关于内存管理的一些建议——
1、手动将生成的无用对象,中间对象置为null,加快内存回收。
2、对象池技术 如果生成的对象是可重用的对象,只是其中的属性不同时,可以考虑采用对象池来较少对象的生成。如果有空闲的对象就从对象池中取出使用,没有再生成新的对象,大大提高了对象的复用率。
3、JVM调优 通过配置JVM的参数来提高垃圾回收的速度,如果在没有出现内存泄露且上面两种办法都不能保证内存的回收时,可以考虑采用JVM调优的方式来解决,不过一定要经过实体机的长期测试,因为不同的参数可能引起不同的效果。如-Xnoclassgc参数等。

推荐的两款内存检测工具
1、jconsole JDK自带的内存监测工具,路径jdk bin目录下jconsole.exe,双击可运行。连接方式有两种,第一种是本地方式如调试时运行的进程可以直接连,第二种是远程方式,可以连接以服务形式启动的进程。远程连接方式是:在目标进程的jvm启动参数中添加-Dcom.sun.management.jmxremote.port=1090 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false 1090是监听的端口号具体使用时要进行修改,然后使用IP加端口号连接即可。通过该工具可以监测到当时内存的大小,CPU的使用量以及类的加载,还提供了手动gc的功能。优点是效率高,速度快,在不影响进行运行的情况下监测产品的运行。缺点是无法看到类或者对象之类的具体信息。使用方式很简单点击几下就可以知道功能如何了,确实有不明白之处可以上网查询文档。

2、JProfiler 收费的工具,但是到处都有破解办法。安装好以后按照配置调试的方式配置好一个本地的session即可运行。可以监测当时的内存、CPU、线程等,能具体的列出内存的占用情况,还可以就某个类进行分析。优点很多,缺点太影响速度,而且有的类可能无法被织入方法,例如我使用jprofiler时一直没有备份成功过,总会有一些类的错误。