Mybatis
入门
Mybatis的基本使用
1.导入依赖
可以在https://mvnrepository.com/中复制导入pom.xml中相应的依赖
<dependencies>
<!--导入Mybatis依赖-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--导入mysql依赖-->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
2.编写实体类
根据所要执行操作的数据库的内容进行编写,定义表中成员变量,定义get set方法,定义tostring方法便于数据查看
3.编写持久层接口
根据所要执行的数据库操作来定义一个接口,提供可以完成需求的抽象方法
4.新建resourses资源文件夹
- resourses资源文件夹用来存放映射文件和配置文件
- 配置文件的路径要求和其对应的持久层接口位置相同,所以要在resourses资源文件夹下创建同样路径的文件夹
- 创建方式与建包不同,用/ 或者 \ 来分隔各级文件夹
5.编写持久层接口对应的映射文件
映射文件编写要求
命名要与持久层接口名相同,拓展名为xml,创建xml文件用new File
映射文件的创建位置必须要与持久层接口的创建位置相同,即对应的路径一致
映射文件的内容为:
<?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.kaifamiao.dao.IUserinfo">
<!--找到之后要用来使用,映射接口中的方法,接口提供一个selectAll查询方法,所以下面开始实现查询操作-->
<select id="selectAll" resultType="com.kaifamiao.model.IUserDemo">
select * from student
</select>
<!--这里id的值是映射的方法名,resultType结果类型是方法的参数类型-->
</mapper>
6.编写配置文件
创建配置文件要把配置文件的路径放在resourses资源文件夹的最外层路径
配置文件的内容为:
<?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">
<!--属性id用来标识数据源-->
<environment id="mysql">
<!--配置事物的类型-->
<transactionManager type="JDBC"></transactionManager>
<!--数据库连接方式-->
<dataSource type="POOLED">
<!--配置JDBC-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/guojinhao"/>
<property name="username" value="root"/>
<property name="password" value="12345"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--配置映射文件的位置(要/相对路径)-->
<mapper resource="com/kaifamiao/dao/IUserinfo.xml"/>
</mappers>
</configuration>
这里发现映射文件和配置文件的头很相似
<!--映射文件mapper——>
<?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">
<!--配置文件config——>
<?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">
改变了关键字
7.编写测试类
先在持久层接口的方法上生成test测试类
单元测试JUit4 的方法需要添加@Test注解
测试类的内容为:
package com.kaifamiao.dao;
import com.kaifamiao.model.IUserDemo;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import static org.junit.Assert.*;
public class IUserinfoTest {
@Test
public void test() throws IOException {
//1读取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2创建SqlsessionFactory的构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3创建创建SqlsessionFactory对象,通过构建者对象来创建
SqlSessionFactory factory = builder.build(in);
//4SqlsessionFactory工厂生产创建Sqlsession对象
SqlSession session = factory.openSession();
//5通过Sqlsession创建dao接口的代理对象
IUserinfo userinfo = session.getMapper(IUserinfo.class);
//6通过代理对象来执行查询方法
List<IUserDemo> users = userinfo.selectAll();
for (IUserDemo user : users){
System.out.println(user);
}
//7释放资源
session.close();
in.close();
}
}
优化
作用域和生命周期
SqlsessionFactoryBuilder
创建SqlsessionFactory之后就不再需要
可以在static静态代码块内使用——初始化之后就不再使用(用完即弃)
SqlsessionFactory
被创建之后要一直存在,可以把他看做池的概念,而Session可以视为池中的内容
可以声明为静态成员,实例完成就一直存在
Sqlsession
他的实例不是线程安全的,不能被共享
可以在方法内部进行实例,随用随取
并且用完之后记得关闭,防止占用资源
sqlsession.close()
优化后项目结构
main文件夹
在持久层dao层和实体类层pojo层同级增加工具类层utils层,并且新建工具类MybatisUtils
在工具类中对Sqlsession提供实例化方法
public class MybatisUtils {
//由于他们的作用域与生命周期
//SqlSessionFactory定义为成员,一直都要存在
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resourse = "mybatis-config.xml";
InputStream stream = Resources.getResourceAsStream(resourse);
//SqlSessionFactoryBuilder在静态代码块内,实例完就不需要了
sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
} catch (IOException e) {
e.printStackTrace();
}
}
//把SqlSession的实例放在方法内,需要的时候就引用,防止占用资源
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
补充:提升变量作用域
在实例化SqlSession的方法中需要用到代码块内的sqlSessionFactory ,需要提升sqlSessionFactory 作用域
把sqlSessionFactory 在类中定义为静态成员,方法内就可以调用
这里需要注意静态代码块内对sqlSessionFactory 的实例时,直接用对象就可以, sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
加上他的类型会导致空指针异常,因为在成员变量中定义的sqlSessionFactory默认为null
test文件夹
Junit4生成测试类时勾选before after,sqlsession的实例引用可以写在before中,sqlsession的关闭可以写在after中
public class StudentDaoTest {
private static SqlSession sqlSession;
@Before
public void setUp() throws Exception {
sqlSession = MybatisUtils.getSqlsession();
}
@After
public void tearDown() throws Exception {
sqlSession.close();
}
@Test
public void selectAll() {
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
List<Student> students = mapper.selectAll();
for (Student student : students) {
System.out.println(student);
}
}
}
配置
属性优化
在mybatis的核心配置文件mybatis-config.xml中,可以对property属性进行配置
比如
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/guojinhao"/>
<property name="username" value="root"/>
<property name="password" value="12345"/>
//****************************************************
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
他们的区别在于值的别名,可以把属性值的配置看做修改别名,配置完成之后,只需要引用就可以
需要在resources文件夹下新建db.properties文件并添加对所需属性的描述
然后在核心配置文件mybatis-config.xml中添加<properties>
标签,引入外部文件db.properties即可
别名优化
在mapper文件中的CURD方法内需要进行传参或返回值,返回值resultType或参数parameterType的值如果不进行别名优化需要写入实体类的全限定类名
如果在核心xml配置文件内对别名进行配置,就可以直接使用别名
<properties resource="db.properties"/>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
//对student和teacher类型改别名
<typeAliases>
<typeAlias type="com.kaifamiao.pojo.Student" alias="Student"/>
<typeAlias type="com.kaifamiao.pojo.Teacher" alias="Teacher"/>
</typeAliases>
也可以通过package来对整个包下的实体类进行系统别名,默认为实体类类名的大/小写
全局配置优化
在核心配置文件中,setting标签内可以定义全局配置,对整个项目做一定的公共设置
比如开启驼峰命名
开启延迟加载(懒加载)
禁用一级缓存
开启二级缓存
注意
在核心配置文件中,配置有规定的顺序,如果没有按照规定顺序进行配置会报错,正确的顺序为
<properties>
属性
<settings>
全局
<typeAliases>
别名
Mappers映射器
在核心xml配置文件中要对mapper文件进行映射才能获取,每一个mapper.xml文件都需要在核心配置中注册
类路径资源引用
<mappers>
<mapper resource="com/kaifamiao/dao/StudentMapper.xml"/>
<mapper resource="com/kaifamiao/dao/TeacherMapper.xml"/>
</mappers>
class文件(全限定类名方式)
<mappers>
<mapper class="com.kaifamiao.dao.TeacherMapper"/>
</mappers>
package包路径
<mappers>
<package name="com.kaifamiao.dao"/>
</mappers>
ResultMap结果集映射
当数据库中字段与实体类中对应的属性命名不能一一对应时,把实体类作为返回值会导致不匹配的字段的值为null
引入ResultMap,对不一致的值进行具体描述
或者在关系数据库多表联查的时候需要使用
这里的teacher表有tid,tname两个字段,而teacher实体类中的属性是tid和name,name不一致
在mapper.xml映射文件中要对SQL语句做一定的修改
<select id="selectByMap" resultMap="ByMap">
select * from teacher
</select>
<resultMap id="ByMap" type="Teacher">
<id property="tid" column="tid"/>
<result property="name" column="tname"/>
</resultMap>
<id>
指对数据库内主键的描述
<result>
指对数据库内其他键的描述
还有<association>
对对象进行描述(多对一)
<collection>
对集合类型的属性进行描述(一对多)
property 指实体类的属性名
column 指数据库字段名
重点注意
对照关系数据库联查,对于单表查询,如果个别实体类属性与数据库字段不对应,在ResultMap结果集映射中只对不对应字段描述即可
而关系型数据库的联查,必须每一条对应语句都写出来,这是规定,否则无对应表示查询出来
动态SQL
复杂的CURD操作,简单的SQL语句不能完成功能,引入动态SQL便于进行
if
当CURD操作有多个条件,通过if标签来添加条件,必须同时满足才能完成CURD操作
<select id="selectByMap" resultType="Teacher">
select * from teacher
<where>
//每一个条件必须都走一遍
<if test="tname!=null">
tname = #{tname}
</if>
<if test="tid!=null">
and tid = #{tid}
</if>
</where>
</select>
choose(when,otherwise)
当CURD操作有多个条件,满足最靠前的条件即可跳出来执行CURD操作
<select id="selectByMap" resultType="Teacher">
select * from teacher
<where>
<choose>
//条件一
<when test="uid!=null">
uid = #{uid}
</when>
//条件二
<when test="uname!=null">
and uname = #{uname}
</when>
//如果条件一条件二不满足就走otherwise 参考java switch default
<otherwise>
and ugrade = #{ugrade}
</otherwise>
</choose>
</where>
</select>
对if和choose中的test值的特别注意
<when test="uname!=null">
and uname = #{uname}
</when>
1.首先test的值 uname!= null 这里的uname不是数据库字段中的uname,而是指传入的参数名,与下边#{uname}是相同的
2.下边uname = #{uname} 前边的uname指数据库中字段uname
3.如果传入的参数不为空,就把这个参数的值添加到条件中
案例:
<select id="selectByMap" resultMap="ByMap" parameterType="Teacher">
select * from teacher
<where>
<if test="name!=null">
tname = #{name}
</if>
<if test="tid!=null">
and tid = #{tid}
</if>
</where>
</select>
<resultMap id="ByMap" type="Teacher">
<id property="tid" column="tid"/>
<result property="name" column="tname"/>
</resultMap>
这里的方法返回值类型为Teacher类型,参数也为Teacher类型,且Teacher类中的name属性对应数据库Tname,不一致
传入的student默认set方法传进去的参数名为实体类中的name,所以if中写的参数不能为uname,需要改成实体类对应的name,并且test的值也要对应为name
trim格式化输出(where,set)
在CURD操作时,多个条件的sql操作需要中间使用连接符
where对应and
因为sql查询条件语句中,where后不能直接接and
定义where标签可以自动剔除他之后的and
set对应,
因为sql更新语句中,set语句最后后不能是逗号,
定义set标签可以自动剔除最后的,
<update id="updateByTea" parameterType="Teacher">
update teacher
<set>
<if test="tgrade!=null">
tgrade = #{tgrade},
</if>
<if test="tname!=null">
tname = #{tname}
</if>
</set>
where tid = #{tid}
</update>