Mybatis(一)-------
mybatis的优势:
1)、消除大量的jdbc冗余代码,包括参数设置、结果封装等
2)、sql语句可控制,方便查询优化,使用灵活
3)、学习成本低,且提供了对spring框架的集成
4)、引入缓存机制,提供了与第三方缓存类库的集成支持
单纯用mybatis开发有接口注解版、xml版
xml:MyBatis开发的详细步骤_骨灰级收藏家的博客-CSDN博客
接口注解:MyBatis学习总结(十一):MyBatis的纯注解开发_给你两窝窝的博客-CSDN博客
这里我们就不用多去关联mysql,而是使用HSQLDB数据库的内存模式作为测试数据库。
一、HSQLDB
mybatis源码中使用了HSQLDB的内存模式作为单元测试数据库(就是数据等都是基于内存),该数据库是使用java编写的,提供了一个小型的同时支持内存和磁盘存储表结构的数据库引擎,支持server模式和内存模式两种方式。
server模式是把HSQLDB作为一个单独的数据库服务运行,类似于我们常见的mysql、oracle等,而内存模式是把HSQLDB嵌入应用中,这儿模式只能存储应用内部数据,但如果应用进程结束,HSQLDB的数据也会丢失,因此这种模式只适合做单元测试。
我们下面开始测试HSQLDB:
pom文件:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
测试类:
package com.blog4java.hsqldb;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.jdbc.ScriptRunner;
import org.apache.ibatis.jdbc.SqlRunner;
import org.junit.Before;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
public class Example01 {
private Connection conn = null;
@Before
public void initData() {
try {
// 加载HSQLDB驱动
Class.forName("org.hsqldb.jdbcDriver");
// 获取Connection对象
conn = DriverManager.getConnection("jdbc:hsqldb:mem:mybatis",
"sa", "");
// 使用Mybatis的ScriptRunner工具类执行数据库脚本
ScriptRunner scriptRunner = new ScriptRunner(conn);
scriptRunner.setLogWriter(null);
//读取resource目录下的文件(依赖的项目的resource目录也会读到)
scriptRunner.runScript(Resources.getResourceAsReader("create-table.sql"));
scriptRunner.runScript(Resources.getResourceAsReader("init-data.sql"));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testHsqldbQuery() {
// SqlRunner是Mybatis封装的操作数据库的工具类
SqlRunner sqlRunner = new SqlRunner(conn);
try {
//调用SqlRunner类的selectAll()方法查询数据
List<Map<String, Object>> results = sqlRunner.selectAll("select * from user");
results.forEach(System.out::println);
sqlRunner.closeConnection();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
sql脚本:
create-table.sql:
drop table user if exists;
create table user (
id int generated by default as identity,
create_time varchar(20) ,
name varchar(20),
password varchar(36),
phone varchar(20),
nick_name varchar(20),
primary key (id)
);
init-data.sql:
insert into user (create_time, name, password, phone, nick_name) values('2010-10-23 10:20:30', 'User1', 'test', '18700001111', 'User1');
insert into user (create_time, name, password, phone, nick_name) values('2010-10-24 10:20:30', 'User2', 'test', '18700001111', 'User2');
insert into user (create_time, name, password, phone, nick_name) values('2010-10-25 10:20:30', 'User3', 'test', '18700001111', 'User3');
insert into user (create_time, name, password, phone, nick_name) values('2010-10-26 10:20:30', 'User4', 'test', '18700001111', 'User4');
insert into user (create_time, name, password, phone, nick_name) values('2010-10-26 10:20:30', 'User5', 'test', '18700001111', 'User4');
insert into user (create_time, name, password, phone, nick_name) values('2010-10-26 10:20:30', 'User6', 'test', '18700001111', 'User4');
insert into user (create_time, name, password, phone, nick_name) values('2010-10-26 10:20:30', 'User7', 'test', '18700001111', 'User4');
insert into user (create_time, name, password, phone, nick_name) values('2010-10-26 10:20:30', 'User8', 'test', '18700001111', 'User4');
insert into user (create_time, name, password, phone, nick_name) values('2010-10-26 10:20:30', 'User9', 'test', '18700001111', 'User4');
insert into user (create_time, name, password, phone, nick_name) values('2010-10-26 10:20:30', 'User10', 'test', '18700001111', 'User4');
insert into user (create_time, name, password, phone, nick_name) values('2010-10-26 10:20:30', 'User11', 'test', '18700001111', 'User4');
insert into user (create_time, name, password, phone, nick_name) values('2010-10-26 10:20:30', 'User12', 'test', '18700001111', 'User4');
insert into user (create_time, name, password, phone, nick_name) values('2010-10-26 10:20:30', 'User13', 'test', '18700001111', 'User4');
insert into user (create_time, name, password, phone, nick_name) values('2010-10-26 10:20:30', 'User14', 'test', '18700001111', 'User4');
log4j.properties:
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=INFO
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c,%L] - %m%n
运行下测试类:
此时就能正常使用HSQLDB了。
二、JDBC
jdbc(java database connectivity)是java提供的访问关系型数据库的接口(当然也可以访问一些文件系统之类的,只要该数据源提供jdbc规范的驱动即可),在java编写的应用里,JDBC API可以执行sql语句、检索sql执行结果、将数据更改写会底层数据源等操作,JDBC API也可以用于分布式、易购的环境下与多个数据域交互。
自1997年java语言引入jdbc规范后,各大数据库厂商开始提供JDBC驱动的实现。
使用JDBC操作数据源大致需要以下步骤:
1)、与数据源建立连接
2)、执行sql语句
3)、检索sql执行结果
4)、关闭连接
----------------------------------------1、建立数据源连接
1、Connection接口
JDBC中定义了一个Connection接口,用来表示与底层数据源的连接,JDBC程序可以用下面两种方式获取Connection对象。
1)、DriverManager(不常用):在JDBC1.0规范就存在,由JDBC API实现的驱动管理类,当程序应用第一次尝试通过URL连接数据源时,DriverManager会自动加载Classpath下的所有JDBC驱动。DriverManager中提供了一些重载方法用来获取Connection对象(getConnection)例如:
Connection con = DriverManager.getConnection("jdbc:hsqldb:men:mybatis", "sa", "");
2)、DataSource(主流):这个接口是在JDBC2.0规范应引入的api,该接口比DriverManager跟受欢迎,因为它提供了更多底层数据源相关的细节,并且应用不在需要去关注JDBC驱动的实现,一个DataSource代表着一个数据源,当DataSource对象的getConnection() 方法被调用会返回一个和数据源建立连接的Connection对象。
我们此时就可以通过修改DataSoucce对象的属性,从而使得该对象获取到指向不同的数据源的Connection对象。
需要注意的是JDBC API只提供了DataSource接口,没有提供DataSource的具体实现,其具体实现由JDBC驱动程序提供(一些主流的数据库连接池Druid、DBCP、C3P0也会提供DataSource接口的具体实现)。
mybatis中有提供了对DataSource接口的实现,我们使用mybatis可以这么去使用:
另外mybatis还提供看DataSource的工厂,即DataSourceFactory(主流),我们可以用工厂模式去创建DataSource实例,例如:
2、其他接口
JDBC API还提供了两个DataSource接口比较重要的扩展,用于支撑企业级应用:
1)、ConnectionPoolDataSource:池化,支持缓存和复用Connection对象,这样能够很大程度提升应用性能和伸缩性。
2)、XADataSource:实例返回的ConnectionSource对象能够支持分布式事务。
注意:JDBC4.0之前的版本,创建Connection对象之前,应用程序需要显式的加载驱动类,具体代码:
Class.forName("org.hsqldb.jdbcDriver");
------------------------------------2、执行sql语句
上面我们已经获取到Connection对象,下一步的需要做的是对目标数据源执行查询和更新操作。 JDBC API提供了一些访问SQL规范常用的实现特性,然而不同的厂商对这些特性的支持程度不同,所以JDBC提供了一个DataBaseMetadata接口,我们可以使用该对象来确认目前使用的数据源是否支持这一特性。(JDBC还提供了转义语法,可以让我们调用各大厂商提供的自己定义的特性)。
获取到Connection对象后,我们可以通过该对象设置事务属性等操作,并且通过通过该接口提供的方法创建出Statement、PreparedStatement、CallableStatement对象。
Statement可以理解为JDBC提供的Sql语句的执行器,可以调用接口中定义的executeQuery、executeUpdate等方法执行查询、修改操作。还可以调用executeBatch方法执行批量处理操作,也可以调用通用方法execute执行查询、修改语句。执行完后会返回一个ResultSet结果集对象。或者我们可以手动的通过getResultSet方法获取结果集,用getUpdateCount方法获取受影响行。
下面是一个执行sql语句的例子:
-------------------------------------3、处理sql执行结果
上面执行完sql后会获取到一个ResultSet结果集对象,我们可以遍历该结果集,然后通过ResultSet提供的一系列getXXX方法去获取想要的数据。
------------------------------------4、使用JDBC操作数据库的完整流程
我们可以用下面的例子来使用jdbc操作数据库:
首先jdbc4.0之前需要显式加载驱动类,然后获取Connection对象,执行sql。获取到ResultSet对象,通过ResultSet对象获取刀片ResultSetMetaData对象(存放 ResultSet 对象中列的类型和属性信息),然后对其进行遍历获取数据信息。最后对连接进行关闭。
此时可以看控制台打印:
ResultSet和ResultSetMetaData的区别:有关ResultSetMetaData与ResultSet_Programming.的博客-CSDN博客
二、JDBC API中的类和接口
jdbc api主要由java.sql、javax.sql两个包构成
1、java.sql包
该包下主要由如下接口、枚举、类:
我们主要需要关心的是下面几个接口:
这些接口都继承java.sql.Wrapper接口。Wrapper接口提供了两个接口为各大厂商提供访问原始类型的功能,从而JDBC驱动中自己定义的一些非标准特性(就是调用驱动提供商自己定义的一些特性扩展接口)
1)、unwrap方法:可以获取未经过保证的JDBC驱动原始类型对象,我们可以通过该对象调用JDBC驱动中提供的非标准方法
2)、isWrapperFor方法:可以判断当前实例是否是JDBC驱动中某一类型的保证类型
下面是一个实例:
上面的代码就是调用oracle驱动中提供的非JDBC标准的方法。
注意:JDBC API中的Connection、Statement、ResultSet等接口都继承Wrapper接口,这些接口都提供了对JDBC驱动原始类型的访问能力
2、javax.sql包
JDBC2.0版本的可选包提供,主要的类、接口等:
1)、DataSource接口
JDBC1.0中使用了DriverManager类来产生一个Connection对象,而JDBC2.0提供了一个DataSource接口用来更好的连接数据源。
首先应用程序不需要像DriverManager一样对加载数据库程序信息进行硬编码,开发人员可以通过JNDI注册这个数据源对象,然后在程序中使用一个逻辑名称来引用它,JNDI会自动根据我们给出的名称找到与这个名称绑定的DataSource对象,然后我们就可以使用这个DataSource对象来建立和具体数据库的连接了。
其次,DataSource接口的第二个优势体现在连接池和分布式事务上。
2)、PooledConnection接口
javax.sql下还提供了一个PooledConnection接口(池化接口),PooledConnection和Connection不同之处在于提供了连接池管理的句柄,前者在使用完之后不用关闭它,有连接池统一管理。
而我们一般开发中不会直接使用这个PooledConnection接口,而是通过第三方管理连接池的中间层(例如Durid),前面我们讲到调用DataSource接口的getConnection方法获取Connection对象,而当我们使用例如Durid第三方连接池中间层时,该对象实际就是PooledConnection类型,每个PooledConnection对象对应一个物理连接。连接池会对这些对象进行统一创建回收等工作,如果不存在可用的PooledConnection对象,连接池管理器会调用ConnectionPoolDataSource对象的getConnection方法创建获取PooledConnection对象。
连接池可以通过调用PooledConnection对象的addConnectionEventListener()将自己注册成一个监听者,当数据库连接需要重用或者关闭时会产生一个ConnectionEvent对象,该对象表示一个连接事件,此时连接池就会接到通知并执行相应实现。
3)、分布式事务相关接口
另外该包下还有一些关于分布式事务的接口:XADataSource、XARsource、XAConnection。XAConnection接口继承PooledConnection接口,因此它具备有PooledConnection的特性。
4)、RowSet接口
Javax.sql中还提供了一个RowSet接口,该接口继承自java.sql下的ResultSet接口,RowSet用于数据源和应用程序的数据的一个映射。RowSet可以建立一个与数据源的连接并在整个生命周期都维持其连接(称为连接的RowSet),也可以连接后获取数据就进行关闭(称为非连接RowSet)然后进行数据操作,操作完成后重新连接数据源将数据更新到数据库(中间减少了占用数据源资源,而是先断开在内存中进行数据操作后再重新连接更新数据源),相较与ResultSet,RowSet的离线操作可以利用应用内存减轻数据库的压力,由于数据操作都是在应用内存中进行的,然后批量提交到数据源,所以性能会有较大的提升。
具体区别:https://www.cnblogs.com/myitnews/p/11846087.html
aaa