JDBC,DBUtils,Hibernate,Mybatis对比分析
前言
作为一名Java后台开发,与数据库打交道是必不可少的一部分,那么与数据库打交道就涉及到了如何建立连接,如何将数据库的表转换为Java对象等等,这些都是需要考虑的问题,所以Java中提供了JDBC来供我们操作数据库
JDBC编程
JDBC(Java Data Base Connectivity),早先的Java开发程序员都是通过JDBC来操作数据库的,通过JDBC来操作数据库有一个很大的弊端就是操作过于繁琐,会有大量的和业务无关的一些操作。通过JDBC来操作数据库主要经过如下步骤:
- 1、注册驱动,并通过指定的数据库url,账号密码等信息来建立连接
- 2、打开连接,获取Connection对象
- 3、通过Connection获取Statement对象
- 4、通过Statement对象操作SQL语句,获得结果集ResultSet对象
- 5、通过ResultSet对象获取结果集,并将结果集转换为Java对象
- 6、关闭数据库连接相关资源
如下就是一个典型的JDBC操作数据库过程:
创建Maven项目并引入Maven相关依赖:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency>
写一个简单的实体类来接收数据库查询结果:
public class LwUser { private String userId; //用户id private String userName; //用户名称 public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } @Override public String toString() { return "LwUser{" + "userId=‘" + userId + ‘\‘‘ + ", userName=‘" + userName + ‘\‘‘ + ‘}‘; } }
然后写一个测试类:
public class TestJDBC { public static void main(String[] args) { Connection conn = null; Statement stmt = null; /**步骤: * 1、注册驱动,并通过指定的数据库url,账号密码等信息来建立连接 * 2、打开连接,获取Connection对象 * 3、通过Connection获取Statement对象 * 4、通过Statement对象操作SQL语句,获得结果集ResultSet对象 * 5、通过ResultSet对象获取结果集,并将结果集转换为Java对象 * 6、关闭数据库连接相关资源 */ try { //1.注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2.打开链接获取Connection对象 conn = DriverManager.getConnection("jdbc:mysql://xxx.xx.xxx.xxx/spring5?serverTimezone=GMT%2B8&useSSL=false", "root", "li199852"); //3、获得Statement对象 stmt = conn.createStatement(); //4.执行sql语句获取结果集ResultSet String sql = "select * from lw_user"; ResultSet resultSet = stmt.executeQuery(sql); //5.将结果转为java对象 while (resultSet.next()) { LwUser lwUser = new LwUser(); String user_id = resultSet.getString("user_id"); String user_name = resultSet.getString("user_name"); lwUser.setUserId(user_id); lwUser.setUserName(user_name); System.out.println(lwUser); } //6.关闭资源 resultSet.close(); stmt.close(); conn.close(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally { if (null != stmt) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (null != conn ) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
链接要加入 ?serverTimezone=GMT%2B8&useSSL=false :
jdbc:mysql://xxx.xx.xxx.xx/spring5?serverTimezone=GMT%2B8&useSSL=false
否者会提示报错:
javax.net.ssl.SSLException: closing inbound before receiving peer‘s close_notify
原因:
原因是MySQL在高版本需要指明是否进行SSL连接。
这就是一个简单的原生JDBC编程的代码,而且是不带参数的查询,如果带参数的话需要进行如下改写:
//有条件查询 String sql1 = "select * from lw_user where user_id = ?"; PreparedStatement preparedStatement = conn.prepareStatement(sql1); preparedStatement.setString(1,"2");//第1个1表示参数位置,第二个是参数的值 ResultSet resultSet1 = preparedStatement.executeQuery();
也就是说我们需要知道参数具体位置,具体类型然后按顺序设置参数。
可以很明显看到,我们只是想执行一个简单的查询,却需要写很多底层操作的和业务无关的逻辑。
具有以下明显的缺陷:
- 1、需要手动管理资源
- 2、代码重复
- 3、业务逻辑与数据操作的代码耦合
- 4、SQL语句需要硬编码写在业务代码内
- 5、设置参数时必须要知道参数具体位置
- 6、结果集需要手动处理
既然这么多和业务无关的代码,那么封装起来就好了,是的,这是一个办法,所以这就有了一些工具类,如DbUtils等来将我们数据操作相关的代码进行了封装
Apache DbUtils
DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,其目的是能够简化JDBC应用程序的开发过程中大量繁琐的数据操作(类似的还有Spring JDBC工具)。
我们先来看一个简单的例子:
首先引入依赖:
<dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.7</version> </dependency>
然后通过DbUtils将上面的JDBC操作进行改写:
import org.apache.commons.dbutils.*; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.tuniu.mybatis.entity.LwUser; import java.sql.*; import java.util.List; public class TestDbUtil { public static void main(String[] args) { try { Connection conn = null; //1.注册JDBC驱动 Class.forName("com.mysql.jdbc.Driver"); //2.打开连接,获取connection对象 conn = DriverManager.getConnection("jdbc:mysql://xxx.xxx.xxx.xxx/spring5?serverTimezone=GMT%2B8&useSSL=false", "root", "li199852"); QueryRunner queryRunner = new QueryRunner(); String sql = "select * from lw_user"; //开启驼峰 GenerousBeanProcessor bean = new GenerousBeanProcessor(); RowProcessor processor = new BasicRowProcessor(bean); //执行查询 List<LwUser> list = queryRunner.query(conn, sql, new BeanListHandler<LwUser>(LwUser.class, processor)); for (LwUser item : list) { System.out.println(item.toString()); } //关闭连接 DbUtils.closeQuietly(conn); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } }
可以看到,获取连接之后我们不需要再去获取Statement和ResultSet等对象了,直接调用API就可以拿到转换成Java对象之后的数据。
使用DbUtils如果执行带参数的查询呢,也是需要进行如下改写:
Object[] params = new Object[1]; params[0] = "1"; List<LwUser> list = queryRunner.query(conn,sql,new BeanListHandler<LwUser>(LwUser.class,processor),params);
相比较于原生的JDBC操作,使用DbUtils已经简单了很多,DbUtils解决了一些问题,但是也有一些问题没有解决。
DbUtils解决了:
- 1、方法封装
- 2、支持数据源
- 3、映射结果集
但是DbUtils仍然有如下2个主要问题没有解决:
- 1、SQL语句硬编码
- 2、参数只能按顺序传入(占位符)
- 3、没有提供缓存等功能(每次操作都需要操作数据库连接,非常消耗性能)
所以为了改进工具类的不足,基于ORM模型的框架就应运而生了。
ORM模型
ORM,Object Relational Mapping:对象关系映射。
ORM模型简单来说就是数据库表和Java对象(Plain Ordinary Java Object,简称POJO)的映射关系模型,如下图所示:
有了ORM框架,开发的时候再也不用去关心底层的数据库操作,只需要专心的写业务代码就行了,大大提升了开发效率。
常见的ORM框架
有了ORM模型,那么就会有人将其落地实现,所以也就有了各种类型的框架。
Hibernate
Hibernate在Java开发中应该可以称之为著名的ORM框架之一了,很多人都用过SSH(Struts2+Hibernate+Spring)框架组合开发,现在用的人可能比较少了,但是在以前Hibernate是开发首选的ORM框架。
Hibernate之所以能流行是因为在Hibernate之前Sun公司曾经推出过一个Java服务端组件模型EJB(Enterprise Java Beans),但是EJB配置比较复杂,而且适用范围小。所以Hibernate诞生后就替代了EJB模型。
Hibernate是通过一个hbm.xml配置文件来实现了数据库和POJO映射,不论是一对一,一对多还是都对多,都可以通过配置文件实现。
Hibernate虽然解决了传统JDBC编程锁存在的一些问题,但是同时也存在以下两个较严重的问题:
- 1、对多表关联和比较复杂的数据表结构查询支持相对较差。
- 2、SQL语句是自动生成的,我们很难修改其生成的SQL语句,也就是无法对SQL进行很好地优化,这对当今互联网公司的数据量级别是无法接受的。
- 3、Hibernate是全表映射,在某些场景中,操作大表会浪费带宽
MyBatis
为了解决Hibernate框架所存在的问题,MyBatis出现了。
MyBatis的前身是Apache的一个开源项目iBatis,并且在2010年正式脱离了Apache,并命名为Mybatis。
Mybatis是一款半自动化的ORM框架,因为要实现数据库和和Java对象的映射需要我们自己去手动实现(Hibernate中并不需要),使用MyBatis,我们需要提供以下三类文件:
- SQL语句
- 映射规则
- POJO
SQL语句需要手动编写,这就给了我们优化的机会,而在Mybatis中,编写SQL语句和传统JDBC不一样的是传统JDBC是直接写在业务代码里,而MyBatis是相对独立的。
MyBatis的映射模型如下图所示:
MyBatis简单示例
接下来我们来看一个MyBatis的简单例子:
1、引入MyBatis依赖:
<!--通过mybatis操作数据库--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <!-- maven编译xml文件需要手动添加配置,否则会找不到mapper.xml文件--> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**\\/*.xml</include> </includes> </resource> </resources> </build>
配置mybatis-config.xml进行全局配置:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- 打印查询语句 --> <setting name="logImpl" value="STDOUT_LOGGING"/> <!--是否开启驼峰命名自动映射--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <!--配置实体类的别名,以后就xml中就不需要写全类名了--> <typeAliases> <typeAlias type="org.tuniu.mybatis.entity.LwUser" alias="LwUser"/> <!--也可以一劳永逸,以后需要的时候就去该包下面找,名字默认类名称首字母小写--> <!-- <package name="org.tuniu.mybatis.entity"/>--> <!--也可以通过注解@Alias("LwUser")指定类名称--> </typeAliases> <environments default="dev"> <environment id="dev"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://xxx.xxx.xxx.xxx/spring5?serverTimezone=GMT%2B8&useSSL=false"/> <property name="username" value="xxx"/> <property name="password" value="xxx"/> </dataSource> </environment> <environment id="prd"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://xxx.xxx.xxx.xxx/spring5?serverTimezone=GMT%2B8&useSSL=false"/> <property name="username" value="xxx"/> <property name="password" value="xxx"/> </dataSource> </environment> </environments> <!--要加载的xml文件--> <mappers> <mapper resource="org/tuniu/mybatis/mapping/UserMapper.xml"></mapper> </mappers> </configuration>
对应的mapper接口文件:
package org.tuniu.mybatis.mapper; import org.tuniu.mybatis.entity.LwUser; import java.util.List; public interface UserMapper { List<LwUser> selectAllData(); }
对应的UserMapper.xml文件,注意,这两个文件要保持同名(UserMapper.java和UserMapper.xml):
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- 命名空间就是对应的mapper接口 --> <mapper namespace="org.tuniu.mybatis.mapper.UserMapper"> <!-- 因为定义了别名(typeAliases定义的),所以这里可以直接使用lwUser--> <select id="selectAllData" resultType="lwUser"> select * from lw_user </select> </mapper>
最后新建一个测试类来试试:
package org.tuniu.mybatis; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.tuniu.mybatis.entity.LwUser; import java.io.IOException; import java.io.InputStream; import java.util.List; public class TestMyBatis { public static void main(String[] args) throws IOException { String resource = "org/tuniu/mybatis/mybatis-config.xml"; //读取mybatis-config配置文件 InputStream stream = Resources.getResourceAsStream(resource); //创建SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream, "dev"); //创建SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); try { List<LwUser> list = sqlSession.selectList("org.tuniu.mybatis.mapper.UserMapper.selectAllData"); for (LwUser lwUser : list) { System.out.println(lwUser.toString()); } } finally { if (null != sqlSession) { sqlSession.close(); } } } }
如此一个简单的demo就完成了。
也可以使用外部配置文件db.properties:
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://xxx.xxx.xxx.xxx/spring5?serverTimezone=GMT%2B8&useSSL=false jdbc.username=root jdbc.password=li199852
config:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties resource="db.properties"></properties> <settings> <!-- 打印查询语句 --> <setting name="logImpl" value="STDOUT_LOGGING"/> <!--是否开启驼峰命名自动映射--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <!--配置实体类的别名,以后就xml中就不需要写全类名了--> <typeAliases> <typeAlias type="org.tuniu.mybatis.entity.LwUser" alias="LwUser"/> <!--也可以一劳永逸,以后需要的时候就去该包下面找,名字默认类名称首字母小写--> <!-- <package name="org.tuniu.mybatis.entity"/>--> <!--也可以通过注解@Alias("LwUser")指定类名称--> </typeAliases> <environments default="dev"> <environment id="dev"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> <environment id="prd"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://xxx.xxx.xxx.xxx/spring5?serverTimezone=GMT%2B8&useSSL=false"/> <property name="username" value="xxx"/> <property name="password" value="xxx"/> </dataSource> </environment> </environments> <!--要加载的xml文件--> <mappers> <mapper resource="org/tuniu/mybatis/mapping/UserMapper.xml"></mapper> </mappers> </configuration>
附项目结构图:
总结
本文通过将传统JDBC,DBUtils,Hibernate,Mybatis等进行了对比,分析了为什么最后MyBatis会成为当前最流行的ORM框架,并利用了一个简单的demo来完成了一个简单的查询语句。
下一篇会详细介绍MyBatis的全局配置文件都可以配置哪些功能。
参考链接:mybatis官网
————————————————
版权声明:本文为CSDN博主「双子孤狼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zwx900102/article/details/108410214
【MyBatis系列1】MyBatis快速入门demo(基于传统JDBC,DBUtils,Hibernate的对比分析)