BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / java / #52928同步于 2016/9/14
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Java机器人发帖

hibernate 为什么好意思叫orm嘛。。不理解啊

icybee
2016/9/14镜像同步10 回复
作为一个常年使用python的同学,始终看不惯ssh那一套超级冗长的裹脚布,这里以orm为例,说一下ssh里的h让我觉得很不爽的地方,来来来,谁来开导一下我 假设某君A使用ssh做web开发,那么在数据表定义和查询这一块A君要做哪些事呢? # hibernate 定义和使用表的蛋疼流程 (ref:http://wiki.jikexueyuan.com/project/hibernate/examples.html) ## 首先,他要创建一个 POJO 类,告诉自己一下表结构 ``` public class Employee { private int id; private String firstName; private String lastName; private int salary; public Employee() {} public Employee(String fname, String lname, int salary) { this.firstName = fname; this.lastName = lname; this.salary = salary; } // 省略一堆getter,setter } ``` ## 然后,他要写sql,告诉数据库表结构 ``` create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) ); ``` ## 再然后,他要写 xml再次定义表结构,告诉框架表结构(使用注解的话这一步可以省略) (ref:http://www.javaworld.com/article/2072999/data-storage/get-started-with-hibernate.html) ``` ... <hibernate-mapping> <class name="Employee" table="EMPLOYEE"> <meta attribute="class-description"> This class contains the employee detail. </meta> <id name="id" type="int" column="id"> <generator class="native"/> </id> <property name="firstName" column="first_name" type="string"/> <property name="lastName" column="last_name" type="string"/> <property name="salary" column="salary" type="int"/> </class> </hibernate-mapping> ``` ## 然后,他发现自己还是要管理session,Transaction这些东西,于是他又封装了一个接口 ``` /* Method to CREATE an employee in the database */ public Integer addEmployee(String fname, String lname, int salary){ Session session = factory.openSession(); Transaction tx = null; Integer employeeID = null; try{ tx = session.beginTransaction(); Employee employee = new Employee(fname, lname, salary); employeeID = (Integer) session.save(employee); tx.commit(); }catch (HibernateException e) { if (tx!=null) tx.rollback(); e.printStackTrace(); }finally { session.close(); } return employeeID; } ``` ##妈了个屯,同样的信息重复了四次,那么多代码才搞定了个sql查询?? 而且,这TM只是好戏的开始,当A君改变数据库结构的时候,才发现噩梦来了,所有的接口都要改一遍,sql也要改,xml也要改,java代码至少有两个地方要改。。。这TM的叫ORM? 现在假设A君用django(python web框架)的orm来搞这些东西呢? # 用django的orm定义和使用表的爽的一B的流程 ## 定义表 ``` from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) ``` ## 插入和查询 ``` p = Person(name="Fred Flintstone", shirt_size="L") # 插入 Place.objects.filter(name="Bob's Cafe") #查询 ``` 妈蛋同样是ORM,为什么django的orm这么爽嘛,还不说django迁移的时候完全不需要写sql,就migration一下就行了,反观Hibernate: The DDL generation in Hibernate does not offer support for data migration at all, if you only do as much as adding a non-null field, DDL generation cannot help you. (ref: http://stackoverflow.com/questions/3923574/jpa-hibernate-support-for-migration) 倒不是针对java,同样是java框架,我最近刚发现的playframework使用的orm Ebean就做得比Hibernate好很多啊 # Ebean 定义和使用表的流程 (ref: http://ebean-orm.github.io/) ## Ebean定义表 ``` @Entity @Table(name="be_customer") public class Customer extends BaseModel { @Column(length=100) String name; @ManyToOne(cascade=CascadeType.ALL) Address billingAddress; @OneToMany(mappedBy="customer", cascade=CascadeType.PERSIST) List<Contact> contacts; ... ``` ## Ebean使用表 ``` LocalDate lastWeek = ...; List<Customer> customers = new QCustomer() .billingAddress.city.equalTo("Auckland") .status.isEqualTo(Status.NEW) .registered.before(lastWeek) .name.istartsWith("rob") .setMaxRows(10) .orderBy() .name.asc() .id.desc() .findList(); ``` 而且playframework也内置了数据库迁移模块evolutions,虽然我觉得没有django的orm那么方便,但是总是有的。。 所以最后问题就来了,为什么hibernate在有这么多不方便的情况下仍然有那么多人坚守嘛 ps,我并不是唯一这么想的: > ORM: When I first started using Hibernate as an ORM, I thought it was great, it had it's problems, and it wasn't perfect, but it was better then what I was doing before. I was happy with it, until I did an application with Django's ORM on a python project, and that opened up my eyes, that is how an ORM is supposed to work. After that project I went back to hibernate, and I just felt disappointed, and longed for going back to Django's ORM. Another great python ORM is sqlalchemy, which is similar to Django's ORM, but a little different. I have limited experience with ROR's ORM, but from what I remember, it was pretty good as well. ------ref :http://programmers.stackexchange.com/questions/102090/why-isnt-java-used-for-modern-web-application-development
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
nuanyangyang机器人#1 · 2016/9/14
SQL是个动态语言(每个SELECT语句,结果的类型都可以不一样),但Java却是静态的,而且这中间还要涉及一次跨语言的通信。 如果用Python,就算不用ORM,直接用SQL,也非常爽:SELECT的结果可以直接用for迭代,每行就是一个tuple。一切都是Python的基本数据结构。一切都是那么自然。 但Java不支持tuple!虽然有ResultSet,但毕竟是一个proxy而不是数据结构。用Array<Object>也行,但Java程序员不喜欢动态类型,而且毕竟一切都装箱成Object是有代价的。要把它转换成具体的bean对象吧……SQL不认识Java啊,所以这个映射必须得人来写。而且Java是OO的语言啊,OO的语言用“引用”来指代别的对象,但SQL是“关系代数”啊,关系代数里只有ID(foreign key)。 用了Java就知道:凡是想把什么东西写到硬盘上,“引用”和“指针”早晚会成为自己的绊脚石:Java的GC认识“引用”,C进程在一个地址空间里耶认识“指针”,但一旦写到硬盘上,唯一可以依靠的就是id、offset、文本名称什么的。SQL更偏向存在磁盘上的数据格式,这并不是高级语言程序猿想看到的。但用惯了高级语言的人总是追求某种纯粹性:如果这个看上去就像“引用”多好?如果我可以把foreign key变成指向别的对象的引用多好? 我看就是Java和SQL之间的语义不匹配。把关系代数往OOP上靠,就像把圆形的积木往方形的洞里放一样。 如果用Scala就好多了。Scala有真的tuple,而且还是静态的。给一点简单的描述,不难生成一段代码把ResultSet里的各个列放到tuple里的每个元素里去。然后,就是说服自己接受“Int可以当foreign key用,并不是所有的东西都必须是引用”。如果单纯的Int不舒服,就封装一下: case class Student(id: Key[Student], name: String, age: Int, teacher: Key[Teacher]) case class Teacher(id: Key[Teacher], name: String, course: String) class Key[T](val v: Int) extends AnyVal class Database { def getStudent(id: Key[Student]): Student def getTeacher(id: Key[Teacher)): Teacher } 查到学生,想找其老师的信息,说服自己,再做一次查询,就好了。并不是非得println(student.teacher.course) 然后还有“绑定时间”的问题:SQL中,整个SQL语句都是解析的时候把各个语法成分分析清楚的。所以,如果是使用存储程序(stored procedure),SQL服务器里可能直接就存着AST,甚至编译好的用于执行这个SQL语句的机器码。这个编译过程可以在程序执行以前(起码是表建好以后)就做好。但是,如果用 List<Customer> customers = new QCustomer() .billingAddress.city.equalTo("Auckland") .status.isEqualTo(Status.NEW) .registered.before(lastWeek) .name.istartsWith("rob") .setMaxRows(10) .orderBy() .name.asc() .id.desc() .findList(); 这样的方式来做查询,整个查询语句的构建过程发生在运行时,而且每查询一次就要构建整个查询语句。但是,如果把这个工作交给服务器端: runSql("SELECT name, age FROM students WHERE id=?;", studentID); 服务器可以直接把这个字符串编译成一个类似这样的函数: void temporary_function12345(int student_id) { Table *table = ... parallel_for(Chunk *chunk in table) { ... } } 然后用那整个字符串形式的SQL语句做为哈希,来定位这个函数。只要做一些验证(比如数字的范围等),直接调用那个函数就行了。
icybee机器人#2 · 2016/9/14
暖神果然切入点不一样啊,一直是从语言本身来切入问题,但是在ORM的问题里我觉得出问题的不是java语言本身,至少不仅仅是java语言本身,hibernate这个库也是非常有问题的。 python 的确可以每行一个tuple,但是也不一定就必须用tuple来代替,我感觉用一个对象的不同属性反而合理很多啊,django的orm也是这么做的,而一些其它的python 库确实是有使用tuple存放行的,不过那些库也就不是ORM了,仅仅算是sql的python接口,对tuple的处理确实java更加蛋疼,会出现下面这样的代码: ``` while (rs.next()) { int id = rs.getInt("id"); String name = rs.getString("name"); Integer age = rs.getObject("age") == null ? null : rs.getInt("age"); System.out.println(id + ": " + name + " " + age); } ``` 关系代数和编程语言的矛盾我感觉也不仅仅存在于sql和java,应该也是同样存在于sql和python的,总之我还是觉得hibernate 完全可以做的更好,而不是像现在这样用起来这么蛋疼 【 在 nuanyangyang 的大作中提到: 】 : SQL是个动态语言(每个SELECT语句,结果的类型都可以不一样),但Java却是静态的,而且这中间还要涉及一次跨语言的通信。 : 如果用Python,就算不用ORM,直接用SQL,也非常爽:SELECT的结果可以直接用for迭代,每行就是一个tuple。一切都是Python的基本数据结构。一切都是那么自然。 : 但Java不支持tuple!虽然有ResultSet,但毕竟是一个proxy而不是数据结构。用Array<Object>也行,但Java程序员不喜欢动态类型,而且毕竟一切都装箱成Object是有代价的。要把它转换成具体的bean对象吧……SQL不认识Java啊,所以这个映射必须得人来写。而且Java是OO的语言啊,OO的语言用“引用”来指代别的对象,但SQL是“关系代数”啊,关系代数里只有ID(foreign key)。 : ...................
chenxiansf机器人#3 · 2016/9/14
围观学习
cocoyimasa机器人#4 · 2016/9/14
暖神告诉你,改用其他语言就好。 作为前Java Web开发者,我不喜欢SSH中任何一个框架。。。大而全,而且完全没有解放生产力发展生产力,提高生产效率,我是觉得没什么卵用了
ml3615556机器人#5 · 2016/9/14
其实如果够懒的话,很多注解也是不需要的。。 命名符合javabean规范就好 不过这样后面动了数据库改起来就很痛苦,尤其是没有refactor的情况下... anyway,真的很烦
iamluo机器人#6 · 2016/9/15
哈哈哈,用mybatis的路过,更麻烦的是,除此以外还得加DAO和JUnit呢!不过有个叫mybatis-generator的东东,可以生成这些代码
riviera613机器人#7 · 2016/9/15
DJANGO后台无脑。。。一周上手
poiuasd机器人#8 · 2016/9/16
我喜欢java这门语言,但是真的特别讨厌那些笨重冗余的框架。所以java的orm我都是用的自己写的orm,简单操作几行搞定。
dongqing机器人#9 · 2016/9/16
hibernate其实现在也不需要mapping配置文件,有注解啊,只需要编写POJO就行了