需要更简单的Java / SQL数据传输

时间:2008-12-29 22:59:31

标签: java sql jdbc

所以,我正在使用jdbc与MySQL数据库通信。对于许多表和许多查询/视图,我创建了一个类,它封装了表的一行或查询/表结果。对DB的访问返回这样一个类的一个对象(当我确切地知道只有一个匹配的行时)或这些对象的Vector。

每个类都有一个工厂方法,该方法从ResultSet的一行构建一个对象。需要大量的ResultSet.getXXX()方法,以及关于哪个值在哪个列中的详细记账,特别是在更改表/查询/视图布局之后。

创建和维护这些对象是一项无聊,工作密集且令人头脑麻木的任务。换句话说,一种由工具完成的任务。它应该读取SQL(MySQL变体,唉)并生成Java代码。或者,至少,给我一个表/查询/视图的表示(XML?DOM?),允许我自己进行java代码生成。

你能说出这个工具吗?

5 个答案:

答案 0 :(得分:5)

我对你的问题有点困惑。为什么不使用像Hibernate这样的对象关系映射框架?

我曾经有过同样的问题,不得不直接读写很多SQL。最后我开始用Hibernate编写更新的项目并且没有回头。系统负责构建实际表并在后台运行SQL,我可以使用java对象。

答案 1 :(得分:2)

如果您正在寻找一个简单的框架来帮助编写sql的苦差事,我建议ibatis sql maps。这个框架基本上完全符合你的要求。

Hibernate也是一个不错的选择,但对于像你这样的简单问题来说它看起来有点过分了。

您可能还要查看spring framework。这旨在为编写Java应用程序创建一个简单的环境,并且还具有非常有用的sql抽象。但要小心春天,你可能会开始喜欢这个框架并花费太多的欢乐时光8)

关于你对反思的关注。 Java在反射的性能开销方面不再存在重大问题(至少从版本1.4开始,使用O / R映射工具)。

根据我的经验,最好关注编写良好且易于理解的代码,而不是关心可能花费的一些性能开销,这只是理论上的。

在大多数情况下,性能问题不会出现在您期望的位置,只能在编写代码后使用代码中使用的测量工具进行识别。性能最常见的问题是与I / O相关或基于您自己的代码中的一些错误(即大量创建具有数百万次运行的类或循环的新实例,这是不必要的......)而不是在jdk本身中

答案 2 :(得分:0)

我创建了一个像多年前那样的迷你框架,但它是用于原型设计而不是用于生产。

这个想法如下,非常简单。权衡是使用反射的成本。虽然Hibernate和其他ORM工具也支付这笔费用。

这个想法非常简单。

  • 您有一个执行查询的Dao类。

  • 阅读ResultSet元数据,您可以在那里获取表名,字段,类型等。

  • 在类路径中查找与表名匹配的类和/或具有相同数量/类型的字段。

  • 使用反射设置值。

  • 返回此对象并将其投射到另一侧并完成。

在运行时找到类似乎很荒谬。并且可能看起来风险太大,因为查询可能会更改或表结构可能会更改。但想一想。当发生这种情况时,您必须更新映射以匹配新结构。因此,您只需更新匹配的类,并对此感到高兴。

我不知道ORM工具如何降低反射调用成本(因为映射它唯一能做的就是帮助你找到匹配的类)在我的版本中查找大约30,000个类(我添加了jar从其他地方来测试它)只花了.30毫秒或类似的东西。我在缓存中保存了该类,第二次没有进行查找。

如果你有兴趣(还在读书),我会尝试在旧电脑上找到这个库。

最后我的代码是这样的:

 Employee e = ( Employee ) MagicDataSource.find( "select * from employee where id = 1 ");

Employee[] emps = ( Employee[] ) MagicDataSource.findAll("select * from employee ");

里面就像是:

Object[] findAll( String query ) {
     ResultSet rs = getConnection().prepareStatemet( query ).executeQuery();
     ResultSetMetaData md = rs.getMetadata();
     String tableName = md.getTableName();

     String clazz = findClass( toCamelCase( tableName ) ); // search in a list where all the class names where loaded.
     Class.forName( clazz );

     while( rs.next() ) {

         for each attribute etc. etc. 
             setter...
         end

         result.append( object );
     }

    return result.toArray();
 }

如果有人知道ORM工具如何处理反射成本,请告诉我。我从开源项目中读取的代码并没有尝试对它做任何事情。

最后,它让我为系统监控创建快速的小程序或类似的东西。我不再做那份工作了,现在已经忘记了lib。

答案 3 :(得分:0)

除了ORM ......

如果您正在使用rs.getString和rs.getInt例程,那么如果依赖命名列而不是编号列,则可以减轻维护负担。

特别是rs.getInt(“id”)而不是rs.getInt(1),例如。

我很少有一个实际的列更改数据类型,因此未来的SQL维护只不过是添加对表执行的新列,而这些列可以简单地添加到怪物的末尾在每个小DAO对象中绑定列表。

接下来,您将使用列名称的习惯用法,并将其扩展到使用一致名称的计划,并且同时使用“唯一”名称。意图是数据库中的每一列都有一个与之关联的唯一名称。从理论上讲,它可以像tablename_columnname一样简单(尽管详细),因此如果你有一个“member”表,列名是id列的“member_id”。

这会给你带来什么?

它使您能够在任何“有效”结果集上使用通用DAO。

“有效”结果集是使用您的唯一命名规范命名的列的结果集。

因此,您将获得“select id member_id,name member_name来自成员,其中id = 1”。

你为什么要那样做?为什么要这么麻烦?

因为那时你的联接变得微不足道。

PreparedStatement = con.prepareStatement("select m.id member_id, m.name member_name, p.id post_id, p.date post_date, p.subject post_subject from member m, post p where m.id = p.member_id and m.id = 123");
ResultSet rs = ps.executeQuery();
Member m = null;
Post p = null;
while(rs.next()) {
    if (m == null) {
        m = MemberDAO.createFromResultSet(rs);
    }
    p = PostDAO.createFromResultSet(rs);
    m.addPost(p);
}

请参阅,此处绑定逻辑不关心结果集内容,因为它只对它关心的列感兴趣。

在你的DAO中,你使它们对ResultSet略显聪明。如果你做了'rs.getInt(“member_id”)'并且member_id实际上不是在结果集中的BE,你会得到一个SQLException。

但是稍微有点工作,使用ResultSetMetaData,你可以做一个快速预检(通过预先获取所有列名),然后调用“rs.getInt”,你可以调用“baseDAO.getInt”,为您处理这些细节,以免获得例外。

这里的美妙之处在于,一旦你这样做,就可以轻松获取不完整的DAO。

PreparedStatement = con.prepareStatement("select m.id member_id from member m where m.id = 123");
ResultSet rs = ps.executeQuery();
Member m = null;
if (rs.next()) {
    m = MemberDAO.createFromResultSet(rs);
}

最后,它真的(真的)是一个简单的脚本(使用,比方说,AWK),它可以获取bean的属性并将其转换为适当的初始DAO绑定代码blob。类似的脚本可以很容易地获取SQL表语句并将其转换为Java Bean(至少是基本成员),然后您的IDE将转换为一系列getter / setter。

通过将绑定代码集中到DAO中,维护实际上几乎没有任何东西,因为它在一个地方发生了变化。使用部分绑定,你可以无情地滥用它们。

PreparedStatement = con.prepareStatement("select m.name member_name, max(p.date) post_date from member m, post p where post.member_id = m.id and m.id = 123");
ResultSet rs = ps.executeQuery();
Member m = null;
Post p = null;
if (rs.next()) {
    m = MemberDAO.createFromResultSet(rs);
    p = MemberDAO.craateFromResultSet(rs);
}
System.out.println(m.getName() + " latest post was on " + p.getDate());

你的负担往往是编写SQL,但即便如此也不可怕。编写SQL和EQL之间没有太大区别。记住,是否有必要编写一个包含数列的select语句,因为你不能(也不应该)使用“select * from ...”(select * always(ALWAYS)lead to麻烦,IME)。

但这些只是现实。不过,我发现(除非您正在进行报告),​​这个问题根本不会发生。对于大多数每个表,它至少会发生一次,但它不会一次又一次地发生。当然,一旦你拥有它,你可以“剪切和粘贴”你的荣耀之路,或者重构它(即sql =“select”+ MemberDAO.getAllColumns()+“,”+ PostDAO.getAllColumns()+ “来自会员m,后p”)。

现在,我喜欢JPA和ORM,我发现它们很有用,但我也发现它们是PITA。那里有一种明确的爱/恨关系。当事情顺利的时候,男孩,它是否顺利。但当它变得崎岖不平时 - 男孩。然后它会变得难看。但总的来说,我确实推荐它们。

但是如果你正在寻找一个“轻量级”非框架,这种技术很有用,实用,开销低,并且可以让你对查询进行大量控制。在您的查询和数据库之间根本没有黑魔法或暗物质,当事情不起作用时,对于某些人的100K行代码中的框架或边缘情况错误情况并不是一些神秘的误解,而是,赔率是, SQL中的错误 - 它所属的位置。

答案 4 :(得分:0)

编辑:没关系。在搜索我的自己的问题的解决方案时,我忘记检查此事的日期。抱歉。您可以忽略以下内容。

@millermj - 你这样做是为了好玩,还是因为有需要?好奇,因为这听起来就像Eclipse和NetBeans等Java IDE已经提供(使用Java Persistence API)New-> JPA->来自Tables的实体类功能。

我可能会忽略这一点,但是如果某个只是需要与其表匹配的类并且是可持久的,那么JPA加上一些IDE“魔术”可能就足够了。