概述
1.原生JDBC
在Java程序里面去连接数据库,最原始的办法是使用JDBC的API。我们先来回顾一下使用JDBC的方式,我们是怎么操作数据库的
// 1.注册 JDBC 驱动
Class. forName ("com.mysql.jdbc.Driver");
// 2.打开连接
conn = DriverManager. getConnection ( DB _ URL ,USER ,PASSWORD );
// 3.获取执行器statement
stmt = conn.createStatement();
// 4.执行查询,处理结果
ResultSet rs = stmt.executeQuery("SELECT bid, name, author_id FROM blog";);
// 获取结果集(ResultSet),并封装具体pojo
while(rs.next()){
// 注:getString,getInt
int bid = rs.getInt("bid");
String name = rs.getString("name");
String authorId = rs.getString("author_id");
}
// 5.关闭连接(逆序)
rs.close();
stmt.close();
conn.close();
使用 jdbc 的问题:
-
重复代码多:这个就是我们通过JDBC 的API 去操作数据库的方法,这个仅仅是一个查询。如果我们项目当中的业务比较复杂,表非常多,各种操作数据库的增删改查的方法也比较多的话,那么这样代码会重复出现很多次。
-
耗费资源多:在每一段这样的代码里面,我们都需要自己去管理数据库的连接资源,如果忘记写close()了,就可能会造成数据库服务连接耗尽。
-
sql与业务逻辑高耦合,扩展性与可维护性差:另外还有一个问题就是处理业务逻辑和处理数据的代码是耦合在一起的。如果业务流程复杂,跟数据库的交互次数多,耦合在代码里面的SQL语句就会非常多。如果要修改业务逻辑,或者修改数据库环境(因为不同的数据库SQL语法略有不同),这个工作量是也是难以估计的
-
结果集(ResultSet)封装重复且复杂:对于结果集的处理,我们要把 ResultSet 转换成 POJO 的时候,必须根据
字段属性的类型一个个地去处理,写这样的代码是非常枯燥的:int bid = rs.getInt("bid"); String name = rs.getString("name"); String authorId = rs.getString("author_id"); blog.setAuthorId(authorId); blog.setBid(bid); blog.setName(name)
2.工具类
2.1 Apache DbUtils
DBUtils 解决的核心类是 QueryRunner,最核心的问题就是结果集的映射,可以把 ResultSet 封装成JavaBean。
-
在 QueryRunner 的构造函数里面,我们又可以传入一个数据源,比如在这里我们 Hikari,这样我们就不需要再去写各种创建和释放连接的代码了
queryRunner = new QueryRunner(dataSource)
-
QueryRunner类对数据库的增删改查的方法进行了封装,我们操作数据库就可以直接使用它提供的方法。
-
那我们怎么把结果集转换成对象呢?比如实体类 Bean 或者 List 或者 Map?在DbUtils里面提供了一系列的支持泛型的ResultSetHandler。
我们只要在DAO层调用QueryRunner的查询方法,传入这个Handler,它就可以自动把结果集转换成实体类Bean或者List或者Map。
String sql = "select * from blog"; List<BlogDto> list = queryRunner.query(sql, new BeanListHandler<>(BlogDto.class));
2.2 Spring JDBC
除了DbUtils之外,Spring也对原生的JDBC进行了封装,并且给我们提供了一个模板方法JdbcTemplate,来简化我们对数据库的操作。
-
我们不再需要去关心资源管理的问题。
-
RowMapper:对于结果集的处理,Spring JDBC也提供了一个RowMapper接口,可以把结果集转换成Java对象。
比如我们要把结果集转换成Employee 对象,就可以针对一个Employee创建一个RowMapper对象,实现RowMapper接口,并且重写mapRow()方法。我们在mapRow()方法里面完成对结果集的处理。
public class EmployeeRowMapper implements RowMapper { @Override public Object mapRow(ResultSet resultSet, int i) throws SQLException { Employee employee = new Employee(); employee.setEmpId(resultSet.getInt("emp_id")); employee.setEmpName(resultSet.getString("emp_name")); employee.setEmail(resultSet.getString("email")); return employee; } }
在DAO层调用的时候就可以传入自定义的 RowMapper 类,最终返回我们需要的类型。结果集和实体类类型的映射也是自动完成的。
public List<Employee> query(String sql){ new JdbcTemplate( new DruidDataSource()); return jdbcTemplate.query(sql,new EmployeeRowMapper()); }
通过这种方式,我们对于结果集的处理只需要写一次代码,然后在每一个需要映射的地方传入这个RowMapper就可以了,减少了很多的重复代码。
RowMapper优化
虽然查询结果只用传入一个RowMapper了,但是还是有问题:每一个实体类对象,都需要定义一个Mapper,然后要编写每个字段映射的getString()、getInt这样的代码,还增加了类的数量。
所以有没有办法让一行数据的字段,跟实体类的属性自动对应起来,实现自动映射呢?当然,我们肯定要解决两个问题,一个就是名称对应的问题,从下划线到驼峰命名;第二个是类型对应的问题,数据库的JDBC类型和Java对象的类型要匹配起来。
我们可以创建一个BaseRowMapper,通过反射的方式自动获取所有属性,把表字段全部赋值到属性。
return jdbcTemplate.query(sql,new BaseRowMapper(Employee.class));
这样,我们在使用的时候只要传入我们需要转换的类型就可以了,不用再单独创建一个RowMapper。
总结
DbUtils和SpringJDBC,这两个对JDBC做了轻量级封装的框架,或者说工具类里面,都帮助我们解决了一些问题。
优点
- 无论是 QueryRunner 还是 JdbcTemplate,都可以传入一个数据源进行初始化,也就是资源管理这一部分的事情,可以交给专门的数据源组件去做,不用我们手动创建和关闭;
- 对操作数据的增删改查的方法进行了封装;
- 可以帮助我们映射结果集,无论是映射成List、Map还是实体类;
问题
- SQL语句都是写死在代码里面的,依旧存在硬编码的问题;
- 参数只能按固定位置的顺序传入(数组),它是通过占位符去替换的,不能自动映射;
- 在方法里面,可以把结果集映射成实体类,但是不能直接把实体类映射成数据库的记录(没有自动生成SQL的功能);
- 查询没有缓存的功能。
3.ORM 框架
要解决这些问题,使用这些工具类还是不够的,要用到ORM框架。
什么是ORM?
ORM的全拼是 Object Relational Mapping,也就是对象与关系的映射,对象是程序里面的对象,关系是它与数据库里面的数据的关系。也就是说,ORM框架帮助我们解决的问题是程序对象和关系型数据库的相互映射的问题。
为什么叫ORM?
O:对象——M:映射——R:关系型数据库
3.1 Hibernate
Hibernate是一个很流行的ORM框架,2001年的时候就出了第一个版本。在使用Hibernate的时候,我们需要为实体类建立一些hbm的xml映射文件(或者类似于@Table的这样的注解)。例如:
<hibernate-mapping>
<class name="cn.gupaoedu.vo.User" table="user">
<id name="id">
<generator class="native"/>
</id>
<property name="password"/>
<property name="cellphone"/>
<property name="username"/>
</class>
</hibernate-mapping>
然后通过Hibernate提供(session)的增删改查的方法来操作对象
//创建对象
User user = new User();
user.setPassword("123456");
user.setCellphone("18166669999");
user.setUsername("qingshan");
//获取加载配置管理类
Configuration configuration = new Configuration();
//不给参数就默认加载 hibernate.cfg.xml 文件,
configuration.configure();
//创建 Session 工厂对象 SessionFactory
factory = configuration.buildSessionFactory();
//得到 Session 对象
Session session = factory.openSession();
//使用 Hibernate 操作数据库,都要开启事务,得到事务对象
Transaction transaction = session.getTransaction();
//开启事务
transaction.begin();
//把对象添加到数据库中
session.save(user);
//提交
Session session.close();
我们操作对象就跟操作数据库的数据一样。 Hibernate的框架会自动帮我们生成SQL语句(可以屏蔽数据库的差异),自动进行映射。这样我们的代码变得简洁了,程序的可读性也提高了。
但仍存在问题:
- 比如使用get()、save() 、update()对象的这种方式,实际操作的是所有字段没有办法指定部分字段,换句话说就是不够灵活。
- 这种自动生成SQL的方式,如果我们要去做一些优化的话,是非常困难的,也就是说可能会出现性能比较差的问题
- 不支持动态SQL(比如分表中的表名变化,以及条件、参数)。
3.2 Mybatis
相对于Hibernate的全自动化,Mybatis是半自动化,也就是说它的封装程度没有Hibernate那么高,不会自动生成全部的SQL语句,主要解决的是SQL和对象的映射问题。
在MyBatis里面,SQL和代码是分离的,所以会写SQL基本上就会用MyBatis,没有额外的学习成本。
MyBatis的核心特性,或者说它解决的主要问题是什么:
- 使用连接池对连接进行管理
- SQL和代码分离,集中管理
- 结果集映射
- 参数映射和动态SQL
- 重复SQL的提取
- 缓存管理
- 插件机制
当然,需要明白的是,Hibernate 和 MyBatis 跟 DbUtils、Spring JDBC 一样,都是对 JDBC 的一个封装,在源码中最后一定会看到 Statement 和 ResultSet 这些对象。
那么问题来了,我们有这么多的工具和不同的框架,在实际的项目里面应该怎么选择?
- 在一些业务比较简单的项目中,我们可以使用 Hibernate;
- 如果需要更加灵活的 SQL,可以使用 MyBatis
- 对于底层的编码,或者性能要求非常高的场合,可以用 JDBC。
实际上在我们的项目中,MyBatis 和 Spring JDBC 是可以混合使用的。当然,我们也根据项目的需求自己写ORM框架。
最后
以上就是幸福大山为你收集整理的操作数据库的三种选择的全部内容,希望文章能够帮你解决操作数据库的三种选择所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复