日期:2014-05-20  浏览次数:20743 次

java十宗罪
java十宗罪


好吧,我知道看这标题很多人就忍不住要拍砖或表示不赞同了,我都接受。

我在遇到问题时,找一些搞java朋友求助,有能解决的,我想说的是不能解决的情况下,他们大多会说:“你怎么能这么弄呢?这么搞是不行的,你首先在思路上就错误了,我们从来就不这么做。”,比如“我们一般很少用存储过程,你用这么多存储过程,我建议你使用hibernate代码实现你的业务,而不是用存储过程”,这个问题是在我遇到用hibernate调存储过程时发生一些状况后,我一个搞java的朋友也无法解决时,他如此说道。
java开发人员还喜欢说:这个不应该由jdk或某某框架完成,而应该是由勇敢、勤劳、智慧的您来编写实现的。

以下都是我在开发java项目时,遇到的一些问题,可能也有写得不对的地方,望指正。
遇到很多问题,一时间无法全部拿出来讨论,就随便列举了几条。

1."abc"=="abc"返回的结果是false,很多初学java的都要在这个问题很浪费很多时间,因为会非常自信的以为这里会返回true,根本不会想到原来问题出在这里。网上看到有贴子讨论过这个问题,说什么java是纯面向对象的语言,==操作符是比较地址什么的,而"abc"是引用,所以不能使用==操作符进行比较,而应该使用equals方法来进行比较,会犯这种错误的,多半是因为自己基础不牢,却还来说java的不是。我看到一个人说的一个例子,很能表示我的感想,是这么说的:有一天我去到一个餐厅,因为餐厅门口有一滩水,导致路面很滑,我不小心摔倒了,于是我找来餐厅经理,追究其责任,结果餐厅经理告诉我,这是因为我自己走路的姿势不对,所以滑倒了,与餐厅无关。

2.为什么没有get;set;属性,而是使用getXX();setXX()方法来代替,反射难道不要成本吗?
  我所了解到的struts\spring\hibernate里都大量用到这种方式,比如struts中的vo对象,里面若有个getUsername(),在页面里可以用<s:property value="username"/>来取值,这中间我想应该是用反射来找到getUsername()方法,再取得其值的吧,同样的spring\hibernate中也大量用到这种方式,我想问,反射不是说很低效的吗?

3.做个自定义标签还要自己写个tld配置文件,有时在看一些java的相关书籍上面提到零配置时,我就觉得非常可笑,不知道所谓零配置的精神到底体现在哪里?也许写个配置文件也并没有那么难,但关键问题在于,从技术上来讲这个tld明显是可以不要的啊,tld里面主要就描述了这个tag叫什么名字有哪些属性,分别是什么类型,这些信息完全可以在类里面表示,只要是实现Tag接口的类就被自动识别为自定义标签这样不好吗?通过识别类里有哪些getXX();setXX();(更好的方案是有get;set;属性)来确定这个自定义标签有哪些属性,又分别是什么类型这样不好吗?为什么还要多此一举搞个tld配置文件?
  还有个问题是,如果我写了个MyTag的类,继承了某个自定义标签类,我还要为MyTag写个相应的tld配置,我不知道将来还有没有其他开发人员会继承我的MyTag,也许就算我热烈欢迎别人来继承我的MyTag,但当别人看到我MyTag里近三四十个getXX();setXX();之后,想到要为其写上相应的tld配置文件而望而生怯吧。

4.数据访问在我看来,应该是个很简单的事情,简单来就,就是执行SQL语句,复杂一点,就再加上实体映射,所有框架解决的问题,首先就是要易于使用,在使用过hibernate之后,我感觉它太复杂了,我在.net里有一个数据访问层,只需要在指定的配置文件中配置连接字符串,就可以在程序中的任何地方调用DbHelper.Execute(sql)、DbHelper.ExecuteDataSet(sql)、DbHelper.ExecuteDataTable(sql)了,使用起来非常简单,当然也有实体映射,DbHelper.save(entity)、DbHelper.delete(entity or key)、DbHelper.select(条件)这一组方法就可以操作实体对象,select返回的是实体列表,实体通过元属性设置其关联的表和字段,这中间除了连接字符串之外,是没有任何其它配置文件的。相比之下,hibernate咋需要这么多配置文件?我知道hibernate也可以配置注解,就不需了hbm配置文件了,但即使这样,据我了解依旧还是需要很多除连接字符串之外的其它的相关配置文件。

5.话说java也有这么多年了,hibernate也有这么多年了,到底是我不会用,还是它真的就是这样的,hibernate对于存储过程的支持,实在让我抓狂,居然不支持存储过程,在网上寻找hibernate调用存储过程,得到的答案多数就是越过hibernate,而仅仅从hibernate中取得一个connection,再使用jdbc的方式调用存储过程,这样做存在一个问题,事务不能得到控制了,由于我还比较水,hibernate的事务控制又是暗箱操作的,好像是只要在service层中写的业务代码就都在一个事务中,所以我无法让我的存储过程调用和hibernate业务代码串在一个事务当中,而很多情况下,我是想要让它们一个失败就全部失败的。
  除此之外,也有不越过hibernate而调用存储过程的办法,有两个,也是要写配置文件,一个是必须要有返回的结果集,我就很纳闷,为什么一定要有结果集,我的很多存储过程就只是处理一些数据,不需要返回结果集的,最难受的是oracle的存储过程其实不支持返回结果集,必须使用一种变态的游标方式返回,这么做我会感觉到极其反胃。另一个办法是通过修改实体在insert\update\delete时的默认行为,比如我在insert一个员工时,本来应该是执行sql语句insert into employee values (?,?,?,?)的,我可以通过配置文件修改这个默认行为,改成{call myproc(?,?,?,?)},这种方式显然也不是我想要的,我只想调用一个存储过程,执行一个业务的处理。以上两种方式是会被暗箱操作的事务所管理的,但并不能满足我的需求,我要怎么办?

6.数据访问的结果集对象ResultSet、RowSet、CachedRowSet等没有得到广泛的应用,各个框架更多的是倾向于支持实体列表,这么做导致出现一个问题,那就是我只能返回已知结构的结果集,若想要临时返回个东西还必须要在实体中添加相应的属性getXX();setXX();方法,比如在hibernate中,要访问员工表,员工表中本来只有部门ID,没有部门名称,你想要有部门名称,就必须在员工实体中添加一个deptName的属性,要所有的结果都是已知结构的,这样很痛苦,如果不返回到实体列表中,也可以返回到 ArrayList<Object[]> 中,但这样的数据没有列名称,不明白为什么不直接查询到ResultSet中,然后让更多的框架支持ResultSet,比如struts,在写页面使用struts标签时,可以像操作实体列表一样操作ResultSet。
<s:iterator id="myResultSet">...</s:iterator>,(还是本来就支持,只是我不会?那就不好意思啦!)只是希望让更多的框架支持未知结构的结果集,让程序员事先设计好结果集的结构是很累人的,就算是代码生成,也只能生成数据库里的每一张表对应的实体,但往往我们需要select unkownSchema from myTable得到未知结构的结果集,并不是每次都select *。

7.再说ResultSet,之所以不直接用这个,而使用实体列表来代替,我想是不是也间接的说明了,ResultSet这个类不方便使用,.net中的DataSet和DataTable就得到大量使用,因为它们方便好用实用。可能最大差别的地方就在于,DataSet是断开式的存在于内存中的微型数据库,而ResultSet只是连接式的数据库读取器,相当于.net中的DataReader,必须保持连接才能读数据,我知道有CachedRowSet可以断开式的存储数据在内存中,好吧,这个就不是问题了。但另一个问题在滋扰着我,做为存储结果集的容器,提供给我们操作这个结果集的方法太少了,甚至取得该结果集的总行数的方法,我们都需要开动小脑筋,这么写:rs.last(); int count = rs.getRow(); rs.first() 负责的话,它需要至少三句代码才能取到总行数。也许这只是小问题,这个或许应该由勇敢、勤劳、智慧的我们来实现。

8.在我看来,struts最大的意义在于,它使得每个jsp页面都有了一个与之对应的java类的方法,也就是那个action方法。你一定会跟我说,struts的功能并不只如此,但我说,我见过的很多(小公司)的项目,struts的意义就只是这样的,我想像在我们国家,还有成千上万的使用java技术的公司,struts对于他们的意义,也就是让jsp有了后台代码。如果仅仅只是如此,为何不由官方提供,直接让jdk支持,让struts的先进来弥补jdk的落后吗?只会欲盖弥彰。