为什么要使用MyBatis?
我们都知道,在学习mybatis之前,要在Java中操作数据库,需要用到JDBC,但是在使用JDBC时会有许多缺陷。
比如:
1、使用时需要先进行数据库连接,不用后要立即释放连接,这样对数据库进行频繁连接和关闭,会造成数据库资源浪费,同时并发量较大时,会影响数据库性能。
解决方案:为了达到连接复用,使用数据库连接池管理数据库连接。
2、将sql语句硬编码到java代码中,使得代码耦合度高,如果sql 语句修改,就需要重新编译java代码,不利于系统维护。
解决方案:将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译,只要在配置文件中修改。
3、向preparedStatement中设置参数,对占位符号位置和设置参数值,硬编码在java代码中,不利于系统维护。
解决方案:将sql语句及占位符号和参数全部配置在xml中。
4、从resutSet中遍历结果集获取数据时,必须保证属性名正确,否则无法取出数据,因此将获取表的字段进行硬编码,不利于系统维护。
解决方案:将查询的结果集,自动映射成java对象。
下面是使用JBDC操作时的代码:
为了方便我使用的是maven项目来编写的,所以在刚开始时需要在pom.xml文件中添加数据库的依赖
<!--添加数据库依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
下面就是代码部分。
public class JDBC {
public static void main(String[] args) {
// 数据库连接
Connection connection = null;
// 预编译的Statement,使用预编译的Statement提高数据库性能
PreparedStatement preparedStatement = null;
// 结果集
ResultSet resultSet = null;
try{
//加载mysql驱动
Class.forName("com.mysql.jdbc.Driver");
String URL="jdbc:mysql://localhost:3306/test";
String USER="root";
String PASS="123456";
// 通过驱动管理类获取数据库链接
connection=DriverManager.getConnection(URL,USER,PASS);
// 定义sql语句 ?表示占位符
String sql="select * from student where SID=?";
// 获取预处理statement
preparedStatement= connection.prepareStatement(sql);
// 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
preparedStatement.setString(1,"2");
//返回结果集
resultSet=preparedStatement.executeQuery();
// 遍历查询结果集
while (resultSet.next()) {
System.out.println(resultSet.getString("Sname")+" "+
resultSet.getString("Sage"));
}
}catch (Exception e){
e.getMessage();
}finally {
//关闭连接
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
通过上述代码可以看出,sql语句和Java语句是混合在一块编码的,这大大更加了代码耦合度,这样在修改sql语句时,都需要重新编译Java代码,不利于系统维护。而且在实际开发中也经常会出现修改或优化sql语句的情况,所以之后人们便更多的使用MyBatis来进行操作数据库。
一,MyBatis概述
MyBatis 本是apache的一个开源项目iBatis,2010年6月由apache software foundation 迁移到了google code,并且改名为MyBatis,因为当时iBatis已经发布到3.x版本了,所以也可以理解为Mybatis实际就是ibatis 3.x的后续版。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--添加mysql数据库依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!--添加mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.6</version>
</dependency>
<!-- 添加log4j 日志依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
</dependencies>
2.添加全局配置文件(mybatis-cfg.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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/StudentMapper.xml"></mapper>
</mappers>
</configuration>
3.根据数据库中要访问的表的信息创建POJO类(Student.java)
public class Student {
private int SID;
private String Sname;
private int Sage;
private String Ssex;
@Override
public String toString() {
return "Student{" +
"SID=" + SID +
", Sname='" + Sname + '\'' +
", Sage=" + Sage +
", Ssex='" + Ssex + '\'' +
'}';
}
public void setSID(int SID) {
this.SID = SID;
}
public void setSname(String sname) {
Sname = sname;
}
public void setSage(int sage) {
Sage = sage;
}
public void setSsex(String ssex) {
Ssex = ssex;
}
public int getSID() {
return SID;
}
public String getSname() {
return Sname;
}
public int getSage() {
return Sage;
}
public String getSsex() {
return Ssex;
}
}
4.创建Mapper接口文件(StudentMapper.java)
这是一个接口类,里面封装了一些空方法,而方法的具体内容一般是在它的映射文件xml中用sql的语法来编写的。
JDBC: Dao(接口) ------> DaoImpl(实现类)
MyBatis: Mapper(接口)-----> xxxMapper.xml
public interface StudentMapper {
public Student selectStudentById(int sid);
}
5.添加Mapper.xml映射文件(StudentMapper.xml)
注意写好的Mapper.xml映射文件一定要注册在全局配置文件(mybatis-cfg.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">
<!--
namespace:名称空间
id:唯一标识
resultType:返回值类型
#{id}:从传过来的参数中取出id值
-->
<mapper namespace="MyBatisDemo.StudentMapper">
<!--查询标签:select 注意此处id为该方法的方法名-->
<select id="selectStudentById" resultType="MyBatisDemo.Student">
select * from student where SID=#{sid}
</select>
</mapper>
6.添加日志配置文件
## debug 级别
log4j.rootLogger=DEBUG,Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target=System.out
log4j.appender.Console.layout = org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d{yyyy-MM-dd-HH\:mm\:ss,SSS} [%t] [%c] [%p] - %m%n
log4j.logger.com.mybatis=DEBUG /
##输出sql 语句
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
7.测试类
public class MyBatisTest {
public static void main( String[] args ) {
SqlSession openSession = null;
try {
//mybatis配置文件
String resourse="mybatis-cfg.xml";
//通过 Resources 工具类将 mybatis-config.xm 配置文件读入 Reader
InputStream inputStream=Resources.getResourceAsStream(resourse);
//通过 SqlSessionFactoryBuilder 建造类使用 Reader 创建 SqlSessionFactory工厂对象
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//通过SqlSessionFactory工厂得到SqlSession
openSession = sqlSessionFactory.openSession();
//通过反射机制来获取对应的Mapper实例
StudentMapper mapper=openSession.getMapper(StudentMapper.class);
//通过mapper调用实例下的方法
Student student=mapper.selectStudentById(6);
System.out.println(student);
} catch (IOException e) {
e.printStackTrace();
}finally {
//最后一定不要忘记关闭 SqlSession ,否则会因为连接没有关闭导致数据库连接数过多,造成系统崩旗
openSession.close();
}
}
}
8.运行结果
四.MyBatis-全局配置文件
1.properties标签
mybatis可以通过properties标签来引入外部properties配置文件的内容,包括两个路径下的资源,
- resource:引入类路径下的资源
- url:引入网络路径或者磁盘路径下的资源
在使用properties标签之前,配置数据库是用下面的方法,由于是使用硬编码的方式,修改value参数值都需要动代码。在使用properties标签后,就可以将参数值封装到一个配置文件中,每次修改参数只需在配置文件中修改即可。
使用properties的步骤:
1.在mybatis-cfg.xml的同目录下创建一个db.properties文件,文件中存放数据库连接使用的一些值,下面是文件中的内容。
2.在mybaties-cfg.xml中添加properties标签,双引号之间是db.properties文件名
3.将mybaties-cfg.xml中的值替换成变量
最后需要注意:如果属性在不止一个地方进行了配置,那么MyBatis将按照下面顺序来加载
- 在property元素体内指定的属性首先被读取
- 然后根据properties元素中的resource属性读取类路径下属性文件或根据url属性指定的路径读取属性文件,并覆盖已读取的同名属性
- 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性
2.settings标签
下图是MyBatis中重要的调整设置,他们会改变Mybatis的运行时行为
举例:驼峰命名法
为了保证查询的数据可以正常显示出来,我们在编写POJO类时,必须保证它内部定义属性名和数据库中的属性名必须相同,但我们在数据库中定义属性时一般习惯采用驼峰命名法A_B,而Java中又习惯采用aB这种格式命名,所以许多人在编写代码时都会因为格式不相同而无法正确查出数据,所以就有了驼峰命名法,这样即使格式不一样(属性名相同,仅格式不同),也可以正确查出数据。
驼峰命名法的使用方式:
在mybatis-cfg.xml文件中添加settings标签即可。
settings用来设置每一个设置项,name设置项名,value设置项取值
3.typeAliases标签(别名处理器)
在mybatis中可以使用typeAliases来给变量取别名,这样对于一些变量名较长的变量,我们就可直接使用它的别名。(别名不区分大小写)
比如:我们每次在StudentMapper.xml中编写查询数据的代码时,都要指定数据返回类型,但又因为返回值类型的名字太长,每次编写时都不方便,所以我们就可以给这个返回值类型起一个简单易编写的别名。
起别名的方法:
1.在mybatis-cfg.xml文件中添加typeAliases标签
2.将StudentMapper.xml映射文件中使用到MyBatisDemo.Student的地方都替换成TMS
批量起别名的方法:
1.在mybatis-cfg.xml文件中添加typeAliases标签
2.在使用该包下的某个类时,就可直接使用类名
但是只用这种方法有个缺陷,就是当该A包底下还存在一个B包,该B包与A包存在相同的类名的类时,使用这种方法就无法区分要使用的是哪个类,所以为了避免这种情况,可结合@Alias注解
4.给MyBatisDemo包下的类名冲突的子类添加注解
3.将StudentMapper.xml映射文件中使用到MyBatisDemo.Student的地方都替换成TMS,将使用到MyBatisDemo.Teacher的地方都替换成TMT
4.typeHandlers标签
5.objectFactory标签
6.plugins标签
7.environments标签
8.databaseidProvider标签
可以支持多个数据库厂商,使用该标签,可以在多个不同的数据库中进行查询数据。
1.在mybatis-cfg.xml文件中添加databaseidProvider标签
2.在StudentMapper.xml映射文件中修改select语句
3.切换当前所使用的数据库环境
9.mappers标签
1.注册一个sql映射,其中resource来指定引用类路径下的sql映射,url用来引用网络路径或者磁盘路径下的sql映射
2.注册接口,使用注册接口的方法和注册sql映射的方法的作用是一样的。
但是使用注册接口的方法,要保证接口的类名和sql映射的文件名相同,并且两个必须放到同一个包路径下。
另外如果没有映射文件,也可使用注册接口的方法,与上面不同的是,sql语句是利用注解写在接口上的,虽然这样利用注解也可以实现,但是sql和java代码的耦合度会更高。所以还是建议利用创建sql映射文件的方式
10.总结
在mybatis-cfg.xml中配置上面的这些标签时,必须按照上面1-9的排列顺序配置,可以缺少某一个标签,但不允许顺序发生变化,否则会报错。
五.XML映射文件的编写
由于xml映射文件和接口文件是绑定的关系,所以每次在xml映射文件中添加sql语句,在接口中也就要添加响应的Java语句。
1.查询操作
接口文件:
sql映射文件:
测试类:
注意此查询仅支持返回一个结果集,如果返回有多个会报TooManyExection异常。所以如果返回结果有多条就需要将接口文件中方法返回值改为List/Map类型,但sql映射文件不变,如下所示。
1.1.List类型
接口文件:
sql映射文件:不变(注意返回类型仍为Student,不为List<Student>)
测试类:
1.2.Map类型(只返回一条记录)
列名作为key,值就是查询的值。
接口文件:
sql映射文件:(返回多天记录时,返回值类型就为记录的类型,但返回一条就可写为map)
测试类:
1.3 Map类型(返回多条记录)
将多条记录封装在一个Map中,并要求指定属性作为Key值,此处是以名字作为key。
接口文件:
sql映射文件:
测试类:
2.添加操作
接口文件:
sql映射文件:
注意:此处student()中的参数格式必须和数据库中的参数格式相同,values()中的#{}中参数必须和POJO类中的参数名相同。
测试类:
此处方法是无返回类型的,所以在返回值类型那块写void即可,如果想改为Boolean/int类型,可直接将void改为Boolean/int即可,其他sql映射代码无需在修改。
3.更新操作
接口文件:
sql映射文件:
注意:此处set后面的参数格式必须和数据库中的参数格式相同,#{}中的参数必须和POJO类中的参数名相同。
测试类:
4.删除操作
接口文件:
sql映射文件:
因为此处是使用(int)sid来查询删除的,参数不为student,所以#{}中可任意写。
测试类:
5.多参数查询
1.使用param来传参。因为在传参数时出现多个参数的情况时,这多个参数会被封装成Map集合,其中key存储为param1.param2....paramn,value存储就是参数的值,所以在sql映射文件中就需要使用param来取值。
接口文件:
sql映射文件:
2.命名参数。使用param来传参难免会有些不便,当参数多了,就会混淆,为了更好区分,我们可以在传参时,使用注解重新命名。这时候key存储为@Param(变量名1).@Param(变量名2)......,value存储就是参数的值,所以在sql映射文件中就需要使用param重新命的名来取值。
接口文件:
sql映射文件:
测试类:
3.如果多个参数正好都是POJO类中的变量,也可以使用POJO类来传参。
4.补充:
6.${}取值与#{}取值的区别
1.#{}是预编译处理,$ {}是字符串替换。
2.MyBatis在处理#{}时,会将SQL中的#{}替换为?号,使用PreparedStatement的set方法来赋值;MyBatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。
3.使用 #{} 可以有效的防止SQL注入,提高系统安全性。
7.自定义结果集映射(resultMap)
1.当POJO类中定义的属性名与数据库中的属性名格式不同时,我们除了可以开启驼峰命名法,还可采用resultMap来解决。
2. 当有两张表Student表和Course表,在学生表中存在课程表的id号的列,那如果想通过Student表即查出学生信息又查出专业信息,我们知道使用resultType是实现不了的,这时候就可使用resultMap来实现。
除了上面这种使用方式,还可以借助association标签来实现。
sql映射文件:
3.使用association标签来实现分布查询
步骤:使用select方法先查出Student的信息,其中包括Cid,然后又根据Cid在Course表中查出课程名。
测试类:
4.延迟加载
通过查看打印结果可知,在上面的分步查询过程中,不过有没有调用student.getCourse()方法,在底层都会进行数据库的两次查询,这样就会降低执行效率,但是使用延迟加载,就可以保证当我们使用了student.getCourse()方法,底层才会取数据库进行查询课程。
开启延迟加载的方法:在全局配置文件(mybatis-cfg.xml)中,添加setting标签。
5.collection标签
有一张teacher表,里面既有教师信息,也有教师授课的信息,如果想通教师号查到该教师所授的课程信息,由于是一对多的关系,因此就需要使用collection标签
六.MyBatis动态SQL
1.if标签
举例:根据传入的学生信息在数据库中查找,如果输入的姓名不为null,就根据姓名查找,如果输入的id不为null,就根据id查找,如果都不为空,就查找两个信息都匹配的学生。
映射文件:
测试类:
通过仔细观察上述代码,就会发现上述代码是存在问题的,如果传入的Sid为空,那么查询就变成了select * from student where and Sname=?,这明显是有语法问题的,那该怎么解决呢?
1.在where后添加1=1
2.将if标签放到where标签中,这样在查询时,就会自动省and,但必须要求and是在赋值语句前面的,如果在后面,必须使用trim标签。
3.在update时后边可能会多一个逗号,可以将更新语句放到set标签里,当然也可以使用trim标签。
2.trim(where,set)
prefix:给整个字符串添加一个前缀
prefixOverrides:去掉整个字符串前面多余的字符
suffix:给整个字符串去掉一个后缀
suffixOverrides:去掉整个字符串后面多余的字符
3.choose(when,otherwise)
对于if标签中举例的那个问题同样可以使用choose解决。
4.foreach 标签
separator:每个元素之间的分隔符。
open:给遍历所有的结果拼接一个开始的字符
close:给遍历出所有的结果拼接一个结束的字符
index:索引。遍历list的时候,index是索引,item就是当前值。遍历map的时候,index就是map的key,item就是map的值。
举例1:给一些id号,然后在数据库中查找与这些id号匹配的学生的信息。
接口文件:
sql映射文件:
测试类:
举例2:批量添加学生信息
接口文件:
sql映射文件:
测试类:
5.bind标签(可以给一个变量绑定上一些字符串)
举例:想通过字符串匹配的方法,通过输入' h ' 找到名字中包含h的学生。
接口文件:
sql映射文件:
测试类:
6.抽取重复的sql语句
来源:oschina
链接:https://my.oschina.net/u/4383725/blog/4285480