一、MyBatis的基础知识
1、概念(什么是MyBatis?)
MyBatis (又称ORM)是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
简单来看,就是这样一个关系:
2、Hibernate和MyBatis的区别(MyBatis的优势是什么?)
3、MyBatis的工作原理
其中各个部分的功能如下图:
名字 | 说明 |
---|---|
配置文件mybatis-config.xml | 用于配置MyBatis运行环境 (主要内容是获取数据库连接),内部可加载多个映射文件 |
映射文件Mapper.xml | 用于配置操作数据库的SQL语句 |
构造会话工厂SqlSessionFactory | 用于创建会话对象SqlSession,代表一个数据库,建议使用单例 |
会话对象SqlSession | 会话工厂的实例化对象,包含数据库中所有执行SQL操作的方法。用于执行持久化操作 |
Executor执行器 | 用于操作数据库(它会根据SqlSession传递的参数动态生成所需执行的SQL语句,还负责查询缓存的维护) |
MappesStatement对象 | Executor的执行方法的一个参数,是映射信息的封装。用于存储要映射的SQL语句的id、参数等。 一个SQL对应一个MappesStatemen,两者id号相同 |
4、MyBatis的核心配置
(1)核心对象
(2)配置文件
(3)映射文件
二、案例分析
创建了一个简单的学生信息管理案例,下图为整个项目的框架:
这是各个文件的作用:
文件 | 作用 |
---|---|
Test.java | 程序入口,拥有main方法,测试类(获取SqlSession对象以及代理对象,调用方法) |
StudentMapper.java | 映射器接口(放各种方法头) |
StudentMapper.xml | 映射器XML文件,描述映射关系、SQL等内容 |
Student.java | POJO对象(普通的学生对象) |
StudentDBUtil.java | 一个工具类,用于创建SqlSessionFactory |
log4j.properties | 日志配置文件,让后台日志数据MyBatis运行的过程日志 |
mybatis-config.xml | MyBatis配置文件 |
1、启动MySQL数据库服务器,执行下列脚本,创建student数据库,做数据库访问前的准备工作
-- student数据库创建示例
create database student;
use student;
create table stu
(
sno char(9) primary key,
sname varchar(30) not null,
ssex char(2) not null,
snative varchar(30),
mno int
);
insert into stu values('100000001','尚小云','女','广东广州',1);
insert into stu values('100000002','廖时飞','男','广东梅州',1);
insert into stu values('100000003','宋凌枫','男','湖南郴州',2);
insert into stu values('100000004','刘小纳', '女','广东佛山',2);
2、访问student数据库
(1)在Eclipse中新建一个Java项目,取名为MybatisTest。
(2)在项目中增加Mybatis框架的核心包、依赖包及MySQL数据库连接包,如下:
(3)在src根目录中加入log4j.properties文件
# \u914D\u7F6E\u6839Logger
log4j.rootLogger=DEBUG, stdout
log4j.logger.org.mybatis=DEBUG
# \u914D\u7F6E\u65E5\u5FD7\u4FE1\u606F\u8F93\u51FA\u76EE\u7684\u5730Appender\u53CAAppender\u9009\u9879, \u8F93\u51FAConsoleAppender\u5230\u63A7\u5236\u53F0
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# \u914D\u7F6E\u65E5\u5FD7\u4FE1\u606F\u7684\u683C\u5F0F\uFF08\u5E03\u5C40\uFF09\u53CA\u683C\u5F0F\u5E03\u5C40\u9009\u9879
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %C %m%n
(4)在com.utils包中新建StudentDBUtil类,用于构造会话对象。代码如下:
package com.utils;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class StudentDBUtil {
private static SqlSessionFactory sqlSessionFactory = null;
static {
try {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (Exception e) {
e.printStackTrace();
}
}
public static SqlSession getSession() {
return sqlSessionFactory.openSession();
}
}
(5)在com.po包中新建Student类(即Java Bean),代码如下(请保持Java Bean的属性和数据表Stu中字段间的一致):
package com.po;
public class Student {
private String sno;
private String sname;
private String ssex;
private String snative;
private int mno;
public String getSno() {
return sno;
}
public void setSno(String sno) {
this.sno = sno;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getSsex() {
return ssex;
}
public void setSsex(String ssex) {
this.ssex = ssex;
}
public String getSnative() {
return snative;
}
public void setSnative(String snative) {
this.snative = snative;
}
public int getMno() {
return mno;
}
public void setMno(int mno) {
this.mno = mno;
}
@Override
public String toString() {
return sno + " | " + sname + " | " + ssex + " | " + snative + " | " + mno ;
}
}
(6)在src根目录中,新建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>
<environments default="mysql_student">
<environment id="mysql_student">
<transactionManager type="jdbc" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/student?characterEncoding=gbk"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/mapper/StudentMapper.xml"/>
</mappers>
</configuration>
(7)在com.mapper包中,定义接口StudentMapper,其方法与StudentMapper.xml文件中的配置信息对应。部分代码如下:
package com.mapper;
import com.po.Student;
public interface StudentMapper {
public Student selectStudentBySno(String sno);
public List<Student> selectStudentBySname(String sname);
public int insertStudent(Student student);
public int deleteStudent(String sno);
public int updateStudent(Student student);
}
(8)在com.mapper包中,新建StudentMapper.xml文件,在文件中编写对stu表进行查询、插入、删除及更新等操作的配置信息。配置信息如下:
<?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 namespace="com.mapper.StudentMapper">
<select id="selectStudentBySno" parameterType="String" resultType="com.po.Student">
select * from stu where sno=#{sno};
</select>
<select id="selectStudentBySname" parameterType="String" resultType="com.po.Student">
select * from stu where sname like concat('%',#{sname},'%');
</select>
<insert id="insertStudent" parameterType="com.po.Student">
insert into stu(sno,sname,ssex,snative,mno) values(#{sno},#{sname},#{ssex},#{snative},#{mno});
</insert>
<delete id="deleteStudent" parameterType="String">
delete from stu where sno=#{sno}
</delete>
<update id="updateStudent" parameterType="com.po.Student">
update stu set sname=#{sname},ssex=#{ssex},snative=#{snative},mno=#{mno} where sno=#{sno}
</update>
</mapper>
(9)在com包中,新建Test类,在main方法编写代码,通过由Mapper接口生成的代理对象访问数据库(测试对stu表的查询、插入、删除和更新等操作)。
package com;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.mapper.StudentMapper;
import com.po.Student;
import com.utils.*;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
SqlSession sqlSession = StudentDBUtil.getSession();
int count = -1;
// 生成Mapper的代理对象studentMapper
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
//精确查询测试
Student student = studentMapper.selectStudentBySno("100000001");
System.out.println(student);
// 模糊查询测试
// List<Student> list = studentMapper.selectStudentBySname("小");
// for (Student stu : list)
// System.out.println(stu);
// 插入记录测试
// Student student = new Student();
// student.setSno("100000006");
// student.setSname("黎明");
// student.setSsex("男");
// student.setSnative("广东肇庆");
// student.setMno(2);
// count = studentMapper.insertStudent(student);
// sqlSession.commit();
// System.out.println("成功插入了" + count + "条记录。");
// 删除记录测试
// count = studentMapper.deleteStudent("100000004");
// sqlSession.commit();
// System.out.println("成功删除了" + count + "条记录。");
// 更新记录测试
Student student = new Student();
student.setSno("100000002");
student.setSname("廖凡");
student.setSsex("男");
student.setSnative("广东汕头");
student.setMno(2);
count = studentMapper.updateStudent(student);
sqlSession.commit();
System.out.println("成功更新了" + count + "条记录。");
sqlSession.close();
}
}
三、遇到的问题和一些想法
1、运行成功,但是出现红色部分的log4j:WARN。解决方法:在src根目录中加入文件log4j.properties即可
2、在成绩表(成绩表)操作时,传递多个参数出错
<delete id="deleteSc" parameterType="String">
delete from sc where sno=#{sno} and cno=#{cno} and tno=#{tno}
</delete>
我的代码如上,但是一直报SQL错误,我查看了数据库的表中的数据段,觉得SQL语句似乎没有错误。
后来查阅博客才发现这个错误的意思是传递多个参数时的写法有错误。正确的写法应该如下:
//注意这里进行多个参数传值时,#{}中内容是arg0,arg1这样的形式
<delete id="deleteSc" parameterType="String">
delete from sc where sno=#{arg0} and cno=#{arg1} and tno=#{arg2}
</delete>
3、为什么要使用工具类?
试想一下,我们在每次做案例,每个方法执行时都要读取配置文件,并根据配置文件的信息构建SqlSessionFactory对象,然后创建SqlSession对象,这导致了大量的重复代码。而且这很不符合Java的封装思想。
4、为什么要用一个Mapper映射器?
这是不使用Mapper映射器的,你会发现它的路径参数很长,“com.mapper.StudentMapper.selectStudentBySno”,而且调用其他方法时,总是要写这么长的一串路径,是不是很麻烦,而且又易错?
Student student = sqlSession.select("com.mapper.StudentMapper.selectStudentBySno", "100000001");
那么,使用Mapper映射器呢?你看代码,是不是一下子感觉清爽了很多?不用写长长的路径了!
Student student = studentMapper.selectStudentBySno("100000001");