目录
在项目开发中,需要进行Java与数据库的联动,然而编写JDBC相关语句实在是繁琐,极大地减缓了开发者的开发速率,因此需要一些框架的支持,来提供这个联动支持,Mybatis就是这样一种开发框架
Mybatis概述
Mybatis 是一款半自动的ORM持久层框架,具有较高的SQL灵活性,支持高级映射(一对一,一对多),动态SQL,延迟加载和缓存等特性,但它的数据库无关性较低
- ORM的意思是对象关系映射,可以在Java对象和数据库表的字段之间形成一种映射,使之能够联系起来,比如有一个Java的Book类,其中有booknum和bookname两个属性,又有一张数据表是book_info,其中有两个字段book_info_num和book_info_name,ORM可以提供booknum和book_info_num的映射关系,从而将这两个不同的软件内容联系起来,并且能够在此基础上进行增删查改操作
- 半自动的意思是虽然能够提供映射关系,不用手撸JDBC的内容,但是还是需要开发者手写SQL语句,因此叫做半自动,也提供了很高的灵活性!
IDEA配置Mybatis
1. 首先需要创建一个maven项目(更方便地管理这个项目的依赖等)
2. 打开创建完的项目的pom.xml,在其中添加mybatis的jar包,具体信息可以在
http:// https://mvnrepository.com/artifact/com.mysql/mysql-connector-j
这个url中找到,这里我直接将其添加到依赖中
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
3. 新建一个xml文件名为mybatis-config.xml,这个文件用于配置该项目的mybatis特性
在文件顶部写上相关的版本信息
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
这里可以使用file template,避免重复编写
在configuration字段中添加environments,设置为mysql数据库类型,并且填入自己的事务管理以及数据库的连接与登录信息等,注意在url字段就是jdbc的填写方法,很简单
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdemo?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="..."/>
</dataSource>
</environment>
</environments>
除了这种方法,还可以在mybatis-config中加入properties标签,将JDBC连接信息写在jdbc.properties文件中,直接用符号引用即可
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatisdemo?characterEncoding=utf-8 username=root password=wujiayi0303
<properties resource="jdbc.properties"></properties>
除此之外,还需要加入一个映射关系map,这个具体在后面会讲到,这里只需要先知道这是一个映射关系,将Java的方法与对应的SQL语句相关联即可,也就是说,有了这张关系映射表之后,调用Java中的方法,会自动执行对应的SQL语句,这张表也需要创建,这里先空着
<mappers>
<mapper resource="mappers/StudentMapper.xml"></mapper>
</mappers>
编写程序
1. 在java文件夹下创建一个StudentDAO接口(dao)和一个Student类(pojo)
2. 编写相关属性与方法
public interface StudentDAO {
public int insertStudent(Student student);
}
public class Student {
private int stuID;
private String stuNum;
private String stuName;
private String stuGender;
private String stuAge;
}
3. 在StudentDAO接口中有一个插入学生的方法,那如何将这个方法实例化并且映射到响应的SQL语句呢,这就需要在之前创建的map.xml文件中进行配置了
打开那个文件,在头部加上一些固定的东西
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
添加<mapper></mapper>,其中namespace是映射到这个表的那个接口类的reference path
<mapper namespace="com.aki.dao.StudentDAO">
</mapper>
然后就能够在其中添加这个StudentDAO接口类中的映射了,添加一个insert,内部编写SQL语句内容,要注意的是values中特别的语法 #{stuNum}
- 这个#{stuNum}的意思是在Java方法的参数中,找到对应属性名的属性,并提取它的值
<insert id="insertStudent" parameterType="com.aki.pojo.Student" useGeneratedKeys="true" keyProperty="stuID">
insert into tb_students(stunum,stuname,stugender,stuage)
values(#{stuNum},#{stuName},#{stuGender},#{stuAge})
</insert>
还有一个需要关注的点就是parameterType,这里指定了参数的类型是com.aki.pojo下的Student,实际上,如果在Java方法中参数已经规定了,这里可以省略
另外,useGeneratedKeys意思是自动回填参数的键值,这个字段是后面keyProperty规定的
此外,如果参数只有一个,实际上在配置文件中不管填什么名字,都会自动匹配到这个
4. 编写测试类,对这个映射进行测试
首先编写要给mybatis_utils,mybatis_utils.getSqlSession()实际上是我自定义的,用会话工厂来获取连接的,其中会话工厂建立在我的配置文件基础上,因此要先读取我的配置文件mybatis-config.xml,这里用ibatis提供的Resources中的getResourceAsStream就能获取
public class mybatis_utils {
private static SqlSessionFactory factory;
private static final ThreadLocal<SqlSession> local=new ThreadLocal<>();
static {
InputStream is = null;
try {
is = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
throw new RuntimeException(e);
}
SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
factory= sqlSessionFactoryBuilder.build(is);
}
public static SqlSession getSqlSession(){
SqlSession sqlSession=local.get();
if(sqlSession==null){
sqlSession=factory.openSession();
local.set(sqlSession);
}
return sqlSession;
}
}
写测试代码,利用SqlSession的getMapper函数实现一个接口类,并且测试其,注意这里要手动提交事务,因此加上sqlSession.commit()
public void testinsertStudent() throws IOException {
//is读取配置文件
SqlSession sqlSession = mybatis_utils.getSqlSession();
StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
System.out.println(studentDAO);
//使用DAO中的方法
Student student = new Student(0, "10005", "吴八", "男", "20");
int i = studentDAO.insertStudent(student);
System.out.println(i);
System.out.println(student);
//手动提交
sqlSession.commit();
}
运行看看测试结果
在数据库中这条数据也是成功添加进去了
5. 这是简单的添加数据操作,如果需要查询数据,可能会遇到一些麻烦
因为查询出的数据通常有多个字段,而在这种映射关系中,如果不去人为规定,机器很难去确定到底哪一个字段应该传到哪一个属性中,这就需要一个工具来提供这种传递的映射关系
好在mybatis提供了一个叫resultMap的映射关系,能够将数据库中的字段与Java类中的属性关联起来!其中id是自定义的,type是规定这个映射到的Java类,column的内容是数据表中的字段,property中的内容是type这个Java类中的属性名
<resultMap id="listStudents_2_map" type="com.aki.pojo.Student">
<id column="sid" property="stuID"/>
<id column="stunum" property="stuNum"/>
<id column="stuname" property="stuName"/>
<id column="stugender" property="stuGender"/>
<id column="stuage" property="stuAge"/>
</resultMap>
随后,在编写select语句时,在resultMap属性中加入这个id就可以了!
<select id="listStudents_2" resultMap="listStudents_2_map">
select sid ,stunum ,stuname ,stugender ,stuage from tb_students
</select>
注意!因为查询动作常常会返回多条数据,因此在Java查询的方法的返回类型也要能够存放多个该类对象,故如下创建即可
public List<Student> listStudents_2();
在测试文件中编写测试方法
@Test
public void testlistStudents_2() throws IOException {
SqlSession sqlSession = mybatis_utils.getSqlSession();
StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
List<Student> list =studentDAO.listStudents_2();
for(Student stu:list){
System.out.println(stu);
}
assertNotNull(list);
}
运行结果如下,返回了各个数据!
值得一提的是,在查询数据时,其实不需要用到事务提交,只需要得到数据即可,因此可以在Mybatis_utils中封装getMapper的函数
public static <T extends Object> T getMapper(Class<T> c){ SqlSession sqlSession=getSqlSession(); return sqlSession.getMapper(c); }
6. 到此为止,我们编写的方法都是一个参数的,如果需要两个参数呢?比如要查询stunum=10009和stuname=王五的数据,又该如何告知mapper哪个参数对应哪个呢
这里就要用到Java提供的@Param了!
public Student queryStudent(@Param("num") String num,@Param("name") String name);
编写mapper
<select id="queryStudent" resultMap="listStudents_2_map">
select sid ,stunum ,stuname ,stugender ,stuage from tb_students where stunum=#{num} and stuname=#{name}
</select>
编写测试文件
@Test
public void testqueryStudent() throws IOException {
SqlSession sqlSession = mybatis_utils.getSqlSession();
StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
Student s =studentDAO.queryStudent("10009","王五");
System.out.println(s);
}
测试结果如下
至此,mybatis入门结束!
事务管理
在上面编写配置文件以及测试程序的过程中,其实已经提到了一些事务管理的方法,比如封装getSqlSession和getMapper的方法,这里会扩展更多
1. 手动提交事务
在之前的例子中,我们都是使用手动提交事务 SqlSession.commit(),每次都要写这一行代码显得很麻烦,有没有上面办法让它自动提交呢,答案是肯定的
2. 自动提交事务
在mybatis的openSession函数中,提供了一个可选参数,若openSession(true),则表示该session的事务是自动提交,无须手动添加(默认设置为false,需要手动commit())
在封装中也可以加入参数,自行设置是否自动提交事务
public static SqlSession getSqlSession(boolean isAutoCommit){
SqlSession sqlSession=local.get();
if(sqlSession==null){
sqlSession=factory.openSession(isAutoCommit);
local.set(sqlSession);
}
return sqlSession;
}
同样的,在getMapper中也可以设置自动提交事务,注意!这里直接设置为true是因为,如果要使用getMapper方法直接获取结果,意思就是不想手动管理事务,因此直接设置为true
public static <T extends Object> T getMapper(Class<T> c){
SqlSession sqlSession=getSqlSession(true);
return sqlSession.getMapper(c);
}
值得一提的是,如果一个方法中有多个SQL操作,建议使用手动提交事务,并且catch异常,进行回滚(rollback)处理,因为如果有多个SQL操作1、2、3,设置为自动提交事务的情况下,操作1完成后直接提交,操作2完成后也直接提交,此时如果操作3产生异常,则只有操作3无法提交,但是没有回滚操作,因此建议只有一个SQL操作时才建议使用自动提交