MyBatis入门知识汇总
为什么要使用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注解
3.给MyBatisDemo包下的类名冲突的子类添加注解
4.将StudentMapper.xml映射文件中使用到MyBatisDemo.Student的地方都替换成TMS
4.typeHandlers标签(类型处理器)
由于java类型与数据库类型之间的差异,无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都需要使用系统提供的类型处理器(也可以自定义类型处理器), 将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。
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语句