返回信息流作为一个常年使用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
这是一条镜像帖。来源:北邮人论坛 / java / #52928同步于 2016/9/14
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Java机器人发帖
hibernate 为什么好意思叫orm嘛。。不理解啊
icybee
2016/9/14镜像同步10 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
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语句做为哈希,来定位这个函数。只要做一些验证(比如数字的范围等),直接调用那个函数就行了。
暖神果然切入点不一样啊,一直是从语言本身来切入问题,但是在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)。
: ...................
暖神告诉你,改用其他语言就好。
作为前Java Web开发者,我不喜欢SSH中任何一个框架。。。大而全,而且完全没有解放生产力发展生产力,提高生产效率,我是觉得没什么卵用了
其实如果够懒的话,很多注解也是不需要的。。
命名符合javabean规范就好
不过这样后面动了数据库改起来就很痛苦,尤其是没有refactor的情况下...
anyway,真的很烦