一、什么是ORM框架?
- ORM(Object Relational Mapping, 对象关系映射),是一个介于面向对象编程语言(如Java、Python)与关系型数据库之间的映射,将数据库的表映射到编程语言所对应的类中,使得有了ORM框架提供数据表到类的映射,数据库的每一条数据就可以通过映射表填入到编程语言中说创建类的对象中,再用编程语言对数据进行处理或者输出(展示到页面),而反过来,编程语言的类也可以ORM框架映射到数据库的对应表中,实现对一个或多个数据表进行动态查询或者处理,甚至可以借助JPA来创建对应的数据表。
- 当前的ORM框架比较出名的就是MyBatis,而MyBatis就是通过简单的XML或注解来配置和映射Java自带数据类型、接口和Java POJO到数据库中的记录中,使得Java能更方便地调取数据库中的数据。
备注:Java POJO(Plain Old Java Object,普通老式Java对象)也就是我们平时封装好的类,该类必须是public,但是该类成员对象私有,用公共方法以提供访问与修改,必须有默认的公共默认构造器方法(无参构造函数),可以没有多个参数的构造器,该类没有继承、接口实现、注解,详情可参考POJO in Java - Javatpoint
二、ORM思想的出现
2.1存在的问题
-
代码冗余
Java程序与数据库的访问比较常用的就是通过JDBC进行连接,而借助JDBC进行数据库处理时,默认都是按照如下步骤进行:1.连接数据库2.编写对应数据的SQL语句3.执行SQL语句。以数据的查询与插入为例。
public List<Teacher> findAll() { Connection connection = null; try { //1.获取数据库连接 connection = Util.getConnection(); //2.创建对应的SQL语句 String sql = "select * from student1"; //3.执行对应的SQL语句 Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql); //将对应的值赋予实例对象 List<Teacher> teachers = new ArrayList<Teacher>(); while (resultSet.next()) { String id = resultSet.getString("id"); String name = resultSet.getString("name"); int age = resultSet.getInt("age"); String institute = resultSet.getString("institute"); String profession = resultSet.getString("profession"); int grade = resultSet.getInt("grade"); int class1 = resultSet.getInt("class1"); id = id.substring(9); Teacher teacher = new Teacher(id, name, age, institute, profession, grade, class1); teachers.add(teacher); } return teachers; } catch (Exception ex) { return null; } }
public void add(Teacher teacher) { try { //1.获取数据库连接 Connection connection = Util.getConnection(); //2.编写SQL语句 String id1 = teacher.getId(); String name1 = teacher.getName(); int age1 = teacher.getAge(); String institute1 = teacher.getInstitute(); String profession1 = teacher.getProfession(); int grade1 = teacher.getGrade(); int class2 = teacher.getClass1(); String sql = "INSERT INTO student1 " + "VALUES(?,?,?,?,?,?,?)"; assert connection != null; //3.执行SQL语句 PreparedStatement ps = connection.prepareStatement(sql); ps.setString(1, id1); ps.setString(2, name1); ps.setInt(3, age1); ps.setString(4, institute1); ps.setString(5, profession1); ps.setInt(6, grade1); ps.setInt(7, class2); ps.executeUpdate(); } catch (Exception ex) { ex.printStackTrace(); } }
可以看到对应结果的处理都是按照三步走,且代码具有相似性,都需要先Util.getConnection()获取数据库连接,然后 PreparedStatement ps = connection.prepareStatement(sql);加载SQL语句,然后如果是修改、新增、删除的话,需要对SQL语句进行execute(),而查找需要executeQuery()获取对应的数据,在获取数据中可以看出代码已经存在很严重的问题——数据库的数据记录无法与程序对象对应。
这样会导致每次查询需要花大量功夫在数据映射上,而非查询数据,这已经使得数据映射的代码量比dao层方法负责获取数据的职责看起来还要多且复杂。不利于之后的代码维护
-
修改困难,更改需求违背开闭原则
提到代码维护有不得不考虑的开闭原则,由于在之前的连接中所有的SQL语句都是从程序中添加书写,如果要更改就会必然会修改原来的代码,一旦修改原来的代码就可能引起项目内其他代码的冲突,维护代码起来相当不方便。就是如下图示意,一旦需要更改一系列的接口的方法,就需要一步一步地按照顺序去一个一个修改。
很显然如果能存在一个接口能直接统一读取对应接口所对应的SQL语句并完成对象的映射,然后再有一个统一的接口去连接数据库并将对应的SQL语句执行。那么最后修改的只是各个接口对应的配置文件,而非原来的代码,原来的代码问题转变为解决统一接口的功能问题,一劳永逸。
-
数据库数据对象与程序实体对象无法映射
这个在查询中尤为明显,当你需要将查询的值返回时,你需要将所有的字段都遍历一遍,当然也可以通过反射简化对应的代码,但终究还是需要一个一个地赋值过去。而且如果存在条件较多的插入、修改等操作,对于SQL的编写有需要极大的代码量去处理。花了大量的代码量却做了一件很显而易见的事情,简单地说吃力不讨好。本身dao层方法就是为了查询或修改对应的数据,却将大量的精力投入到将传入的参数映射到SQL中或者将获取到的值映射到结果集中。导致这样问题产生原因就是我们没有提供一个方法能够完美地将数据的数据或者传入参数的数据与数据库中的数据记录实现对应的映射。
2.2如何解决
因此,ORM就开始着手解决这些问题。
针对代码冗余与修改困难问题,ORM采取使用配置文件隐形接口方法的映射,将对应的查询、修改等方法都封装在一个数据包中。数据包有一个数据库的连接池,他会根据调用方法所对应的配置文件中的SQL语句来选择对应的查询、新增、修改或者删除方法,然后将结果在数据包内完成对象的映射。从而将数据的获取与修改的代码模式化,代码看起来更加简洁。
而针对数据对象的映射,他运用了映射类和映射表,映射类也就是我们的实体类,他会描述数据库表的结构,ORM通过映射表指定数据库表和映射类之间的关系,包括映射类和数据库表的对应关系、表字段和类属性类型的对应关系以及表字段和类属性名称的对应关系等。 框架要做的就是去解析映射表(XML文件)从而获取对应的数据关系,再利用反射进行数据赋值。
总得来说,ORM的作用就是完成对象与关系型数据库的映射,封装底层与数据库的交互,并且很多都提供了强大的附加功能,比如持久化。
ORM工具框架最大的核心就是封装了JDBC的交互,你不在需要处理结果集中的字段或者行或者列
但是既然是JDBC的封装,就势必会带来性能的开销,这是无法回避的事实,不过现在技术不断发展,性能开销越来越小。
备注:持久化简单理解就是脱离内存可以独立保存,保存到数据库,保存到文件等等形式,都是持久化。
参考文章:
JDBC与ORM发展与联系 JDBC简介(九) - noteless - 博客园 (cnblogs.com)
What is ORM? | How ORM Works? | A Quick Glance of ORM Features (educba.com)