解惑

解己之惑,解人之惑

标签:Java (第5页共6页)

条条大路通罗马

今天在写测试的用例的时候发现框架的一个Bug,CMP在初始化Entity Bean的时候会自动初始化相关的Entity Bean,但是如果是在一对多的情况下,首先使用的如果是一那端的情况下,不会自动的初始化多那一端的Entity Bean,后来添加了代码修正这个问题后,发现原来只要2分钟运行的所有的测试用例要15分钟才能运行完了,跟踪了一下发现是其中一个测试用例所使用的Entity Bean会连带初始化其他十几个Entity Bean,而且其中有几个Entity Bean的初始数据非常的多,每一个都有300条数据,这样要初始化好需要3分钟,而每个test方法都会来这么一次初始化。后来想着加Cache,Entity Bean填充好以后就缓存起来,后面的test方法再需要初始化的时候就直接进行对象拷贝就可以了,但是Entity Bean在使用的时候会修改一些值,这样如果发生变化的话应该从Cache里面清除,而且需要连带清除所有的Entity Bean。原来缓存的是MockEJB动态创建的Proxy对象,所以要进行对象复制不容易,看了MockEJB的源代码本来想自己也创建一个新的Proxy对象进行复制保存,但是工作量比较大,后来想到的解决的办法就是把对象的属性都复制到一个Map里面,主键是属性的名字,值就是对象值,如果值是集合类型,那么要创建一个同样的集合类并发那个集合里面的全部值加进去。

上面是我在公司的时候的解决方案,在写这篇日志的时候,又想到可能的其他方法,例如初始化Entity Bean的时候不初始化相关的其他的Entity Bean,只有在需要的时候才初始化(增加一个BaseGetterAspect,就像其他的Aspect一样);另外就是在发布Entity Bean的时候不初始化所有的Entity Bean实例,只有在需要使用到那个实例的时候再初始化。当然这两个也不是那么简单就可以实现的,明天还要再好好的考虑下哪种最保险最简单。

在遇到一个问题的时候,解决方案是多种多样的,只要你敢想。:)

基于MockEJB的CMP实现

MockEJB提供了一个简单的CMP容器实现,但是功能实在是过于简单,只实现了一个findByPrimaryKey方法。它的CMP其它功能的实现需要依赖于Aspect,我的这个单元测试框架就是靠实现了一堆的Aspect来完成CMP的一些功能的。

  1. BaseCreateAspect:调用entity bean的create方法会进入这个Aspect,如果是系统初始化从数据库和文件读取初始数据的话,会直接根据那些数据填充entity bean,如果是程序代码创建的entity bean,就会调用entity bean的create和postCreate并且准备把创建的entity bean的内容同步到数据库。
  2. BasePostCreateAspect:调用entity bean的postCreate会调用这个,目前功能不多。(没有必要,删除)
  3. BaseFindAspect:调用entity bean的find方法会调用这个,前面的finder解析器解析的结果就是由它来使用的。
  4. BaseRemoveAspect:调用entity bean的remove方法会调用这个,会把记录从数据库删除。
  5. BaseSetterAspect:调用entity bean的setter方法会调用这个,记录entity bean的内容有更新,可能会需要同步到数据库。

在完成这些Aspect的时候需要定义拦截点,可以使用表达式,可惜使用的是Apache的ORO的语法,和Java自己带的表达式语法不同。(修改为自己的Pointcut实现

代码如下:
阅读全文

JR总算是开始搞培训了

其实我们很早就想搞培训了,去年也筹备过一次,后来不了了之了。这次在David的努力下总算是有些眉目了,只要报名的人数足够,近期就可以开始了。我也会讲一部分内容,第五天的那些杂乱的内容,期望能够讲好。以前没有搞过,不知道能不能讲好,但是只要准备充分,应该问题不大吧。
希望David能够尽快招到人,顺便也在这里做个广告。

matches和find的用法

Matcher类有几个进行匹配的方法,一个是matches,一个是find,前者是将整个输入和表达式进行匹配,而后者只是查找匹配的部分,只要找到匹配的就返回。一个典型的应用就是分析SQL,我们写一个表达式先判断整个SQL是否匹配表达式,如果匹配的话就要进行分析,取出我们想要的部分,但是WHERE条件部分有很多条件,使用group只能取到最后一次匹配的部分(参考以前写的Java正则表达式的性能问题你写的正则表达式有多复杂? ),这个时候就要针对条件再写一个表达式,使用find查找所有的条件了,代码片断如下:
阅读全文

你写的正则表达式有多复杂?

正则表达式的功能是非常强大的,但是要使用好也是不容易的,为了能够解析Entity Bean的Finder中的SQL,我写了可以解析部分SQL的表达式:
一个是可以支持FROM子句中的IN语法的:
SELECT\s+(DISTINCT)?\s*OBJECT\s*\(\s*(\w+)\s*\)\s+FROM\s+(\w+)(?:\s+AS)?\s+\2\s*,\s*IN\s*\(\s*\2\.(\w+)\s*\)\s*(?:\s+AS)?\s*(\w+)\s+
(?:WHERE\s+((?:(AND|OR)?\s*(\2|\5)((?:\.\w+)+)\s*(=|is|<>|>=|>|<|<=)\s*(NOT)?\s*(\??\d+|null|true|false|empty)\s*)*))?
(?:\s*ORDER\s+BY\s+(\2|\5)((?:\.\w+)+)(?:\s*(asc|desc)?))?\s*
阅读全文

Java正则表达式的性能问题

在编写EJB-QL的解析器的时候偶然发现的。
假设要解析的EJB-QL的值为:
SELECT OBJECT(al) FROM AppLibraryConfig al WHERE al.id=?1 AND al.status=true

下面看看不同的表达式Pattern所需要的执行时间,解析的伪代码为:
long start=System.currentTimeMillis();
Pattern pattern = Pattern.compile(getPatternText(), Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(query);
if (matcher.matches()) {
        int groupCount = matcher.groupCount();
        for (int i = 0; i <= groupCount; i++)
        {
            System.out.println("Group " + i + " :[" + matcher.group(i) + "]");
        }
}
System.out.println(System.currentTimeMillis()-start);

现在看看不同的getPatternText()的值得到的不同结果。
阅读全文

Session ID

下午一个朋友问我HttpServletRequest.getSession().getId()和HttpServletRequest.getRequestedSessionId()的差异,其实我以前也没有研究过这个差异,看了下API的说明,前者应该是属于服务器端的概念,这个值是存在于服务器的,后者是客户端的概念,也就是那个值是浏览器提交的。我们知道HTTP连接是无状态的,那么如何维护一个Session呢?其实就是通过这个HttpServletRequest.getRequestedSessionId(),如果大家细心一些,有时候会发现有时候浏览器的地址栏或者状态栏里面的地址后面会带一个sessionId的参数值,这个应该就是那个HttpServletRequest.getRequestedSessionId()的返回值了。而且这个值一般也会存在cookie里面,这样就避免了在每次请求的时候都带在请求的URL里面或者FORM里面,它是随着浏览器和服务器端的Cookie进行交流,对于用户和开发人员是透明的。

Java的日期的缺省格式

在做那个单元测试框架的时候要进行类型转换,对于日期型的值,先转换为String然后可能会转换回Date类型,但是转换回Date类型的时候一直出错,后来没有办法看JDK的源代码,才知道日期的缺省格式比较变态,你要把那种格式的字符串转换回Date需要使用:

new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US);

很纳闷为什么不把new SimpleDateFormat()的时候就设置为这种格式,非要在Date类的toString方法中使用这样的格式。

Java参数传递方式

其实这个问题我原来翻译(破除java神话之二:参数是传址的 )、转帖别人的详细解释(Java 应用程序中的按值传递语义 )和专门解释( 我对《Java 应用程序中的按值传递语义》的理解 )过,不过现在看来,原来翻译或者解释的角度是有问题的,从底层的角度解释并不直观,在交流的时候也容易引起误解,最终不能达成一致意见。下面以最终的效果来解释参数的传递方式:

1、对于原始数据类型,也就是int、 long、char之类的类型,是传值的,如果你在方法中修改了值,方法调用结束后,那个变量的值没用改变。
2、对于对象类型,也就是Object的子类,如果你在方法中修改了它的成员的值,那个修改是生效的,方法调用结束后,它的成员是新的值,但是如果你把它指向一个其它的对象,方法调用结束后,原来对它的引用并没用指向新的对象。
阅读全文

Java类的完整构造执行顺序

这里只说一个完整的结果,至于为什么是这样的顺序,可以参考我以前的文章:深入剖析java类的构造方式

  1. 如果父类有静态成员赋值或者静态初始化块,执行静态成员赋值和静态初始化块
  2. 如果类有静态成员赋值或者静态初始化块,执行静态成员赋值和静态初始化块
  3. 将类的成员赋予初值(原始类型的成员的值为规定值,例如int型为0,float型为0.0f,boolean型为false;对象类型的初始值为null)
  4. 如果构造方法中存在this()调用(可以是其它带参数的this()调用)则执行之,执行完毕后进入第7步继续执行,如果没有this调用则进行下一步。(这个有可能存在递归调用其它的构造方法)
  5. 执行显式的super()调用(可以是其它带参数的super()调用)或者隐式的super()调用(缺省构造方法),此步骤又进入一个父类的构造过程并一直上推至Object对象的构造。
  6. 执行类申明中的成员赋值和初始化块。
  7. 执行构造方法中的其它语句。

阅读全文

更早的文章 更新的文章

© 2024 解惑

本主题由Anders Noren提供向上 ↑