@教程来源,动力节点
mybatis 是数据访问层(持久层)的一个轻量级框架,增强版的JDBC
MyBatis配置
配置maven依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>groupId</groupId>
<artifactId>frame_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- 导入mabatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!-- 导入mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!-- 导入单元测试依赖-->
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<!-- 将配置文件装载进target目录中-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
配置mybatis.xml全局配置文件
配置文件一般放在main目录下的resources资源目录下
<?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>
<!-- settings,控制全局行为-->
<settings>
<!-- 输出日志,name表示控制日志,value表示输出日志到控制台上-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!-- 默认数据库环境-->
<environments default="development">
<!-- 需要连接的数据库环境,id可以自定义-->
<environment id="development">
<!-- 事务管理方式,type="JDBC",表示底层调用的是JDBC的Connection对象,commit,rollback
也可以type="MANAGER",表示把事务处理委托给其他容器(服务器软件,框架(spring))-->
<transactionManager type="JDBC"/>
<!-- 数据源,实现了javax.sql.DataSource的都是数据源,数据源是连接Connection对象的。数据库连接池-->
<dataSource type="POOLED">
<!-- 连接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/frame_demo?serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 一个mapper对应一个配置文件-->
<mapper resource="amon/dao/StudentDao.xml"/>
</mappers>
</configuration>
mybatis使用
创建实体类
public class Student{
private Integer id;
private String name;
private String email;
private Integer age;
}
参数与数据库表的参数对应,生成构造方法,getter和setter方法,重写toString()
创建实体类对应的Dao接口
ps:不要使用重载方法,因为mapper映射的是方法名,要求唯一
public interface StudentDao {
// 查询表的对象数据集合
public List<Student> selectStudents();
public int insertStudent(Student student);
}
在dao目录下创建Dao接口对应的mapper文件
即StudentDao.xml
这是一个sql映射文件
<?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">
<!--命名空间一般用对应的Dao接口名,-->
<mapper namespace="StudentDao">
<!-- id 可以自定义,要求与dao接口中的方法名一致
resulttype 表示返回的类型,查询结果是Student对象,要求target/classes下的全类名
里面写的是需要执行的sql语句-->
<select id="selectStudents" resultType="amon.domain.Student">
select id,name,email,age from student order by id
</select>
<!-- 插入时values采用占位符形式,用#{属性名}来表示对象对应属性的值-->
<insert id="insertStudent">
insert into student values (#{id},#{name},#{email},#{age})
</insert>
</mapper>
通过mybatis访问数据库
此时完全只数通过dao.xml来访问数据库,dao接口没有任何作用
package amon;
import amon.domain.Student;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyApp {
public static void main(String[] args) throws IOException {
// 访问mybatis,读取数据
//1.定义主配置文件的名称,从tatget/classes的根目录开始
String config = "mybatis.xml";
//2.读取config配置文件
InputStream in = Resources.getResourceAsStream(config);
//3.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//4.创建SqlSessionFactory对象
SqlSessionFactory factory = builder.build(in);
//5.获取SQLSession对象
SqlSession sqlSession = factory.openSession();
//6.指定要执行sql语句的标识
String sqlId = "StudentDao" + "." + "selectStudents";
//7.执行sql语句
List<Student> studentList = sqlSession.selectList(sqlId);
/**
* //如果是插入操作,则需要传入对象
* //而且mybatis默认是不自动提交事务的,所以在insert,delete,updata后要手动提交事务
* //6.指定要执行sql语句的标识
* String sqlId = "StudentDao" + "." + "insertStudent";
* //7.执行sql语句
* Student student = new Student(1004, "赵六", "zhaoliu@126.com", 25);
* int index = sqlSession.insert(sqlId, student);
* sqlSession.commit();
*/
//8.输出结果
for (Student stu:studentList) {
System.out.println(stu);
}
//9.关闭SqlSession对象
sqlSession.close();
}
}
通过Dao接口的实现类访问数据库
封装mybatis工具类
package amon.utils;
import com.mysql.cj.Session;
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 java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
private static SqlSessionFactory factory = null;
static {
String config = "mybatis.xml";
try {
InputStream in = Resources.getResourceAsStream(config);
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
SqlSession sqlSession = null;
if (factory != null){
sqlSession = factory.openSession();
}
return sqlSession;
};
}
创建Dao接口的实现类
package amon.dao;
import amon.domain.Student;
import amon.utils.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class StudentDaoImpl implements StudentDao{
@Override
public List<Student> selectStudents() {
//获取SqlSession对象
SqlSession sqlSession = MyBatisUtil.getSqlSession();
//指定要执行sql语句的标识
String sqlId = "StudentDao.selectStudents";
//执行sql语句
List<Student> studentList = sqlSession.selectList(sqlId);
//关闭SqlSession对象
sqlSession.close();
return studentList;
}
@Override
public int insertStudent(Student student) {
//获取SqlSession对象
SqlSession sqlSession = MyBatisUtil.getSqlSession();
//指定要执行sql语句的标识
String sqlId = "StudentDao.insertStudents";
//执行sql语句
int index = sqlSession.insert(sqlId, student);
sqlSession.commit();
//关闭SqlSession对象
sqlSession.close();
return index;
}
}
测试使用mybatis
public class TestMybatis {
@Test
public void test() {
//创建dao对象
StudentDao dao = new StudentDaoImpl();
//插入一行数据
Student student = new Student(1004, "赵六", "zhaoliu@126.com", 25);
int index = dao.insertStudent(student);
System.out.println("------------------------------------------------------");
System.out.println("index = " + index);
System.out.println("------------------------------------------------------");
//查询所有数据
List<Student> studentList = dao.selectStudents();
System.out.println("------------------------------------------------------");
System.out.println("studentList = " + studentList);
System.out.println("------------------------------------------------------");
}
}
执行结果
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 925308434.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@37271612]
==> Preparing: insert into student values (?,?,?,?)
==> Parameters: 1004(Integer), 赵六(String), zhaoliu@126.com(String), 25(Integer)
<== Updates: 1
Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@37271612]
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@37271612]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@37271612]
Returned connection 925308434 to pool.
------------------------------------------------------
index = 1
------------------------------------------------------
Opening JDBC Connection
Checked out connection 925308434 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@37271612]
==> Preparing: select id,name,email,age from student order by id
==> Parameters:
<== Columns: id, name, email, age
<== Row: 1001, 李四, lisi@qq.com, 20
<== Row: 1002, 张三, zhangsan@sina.com, 28
<== Row: 1003, 王五, wangwu@163.com, 20
<== Row: 1004, 赵六, zhaoliu@126.com, 25
<== Total: 4
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@37271612]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@37271612]
Returned connection 925308434 to pool.
------------------------------------------------------
studentList = [Student{id=1001, name='李四', email='lisi@qq.com', age=20}, Student{id=1002, name='张三', email='zhangsan@sina.com', age=28}, Student{id=1003, name='王五', email='wangwu@163.com', age=20}, Student{id=1004, name='赵六', email='zhaoliu@126.com', age=25}]
------------------------------------------------------
mybatis优化
Dao的实现类中的select与insert方法有很多代码重复,并且sqlId的全限定类名与方法名都与xml配置文件中的namespace,id相同,所以可以运用mybatis自身的动态代理来进一步优化
优化后就不用再创建Dao接口的实现类,之后删除类StudentDaoImpl文件,执行失败,找不到Dao接口,发现是Dao的xml配置文件里的namespace没有用全类名,果然要求是用在这个地方的,给StudentDao前面加上全类名就成功运行了
最后记得在优化后的代码中,执行操作要多一个commit()
public class TestMybatis {
@Test
public void test() {
/*
使用mybatis的动态代理机制,使用SqlSession.getMapper(Dao接口.class)
能够获取到Dao接口对应的实现类对象
*/
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
//com.sun.proxy.$Proxy4,jdk的动态代理
System.out.println(dao.getClass().getName());
//插入一行数据
Student student = new Student(1004, "赵六", "zhaoliu@126.com", 25);
int index = dao.insertStudent(student);
SqlSession.commit();
System.out.println("------------------------------------------------------");
System.out.println("index = " + index);
System.out.println("------------------------------------------------------");
//查询所有数据
List<Student> studentList = dao.selectStudents();
System.out.println("------------------------------------------------------");
System.out.println("studentList = " + studentList);
System.out.println("------------------------------------------------------");
}
}
mybatis进阶
执行语句时传一个或多个参数常用方法
Dao中的方法
// 传入一个简单类型的参数
public Student selectStudentById(Integer id);
// 传入多个简单类型的参数,使用@Param注释
public List<Student> selectStudentsByNameAndAge(@Param("name") String name, @Param("age") Integer age);
// 传入多个参数,使用对象
public List<Student> selectStudentsByNameOrAge(Student student);
Dao的xml配置
<!-- Java中自带的数据类型名称在mybatis中有别名,如
mybatis java
_int int
int Integer
因为数据类型可以通过反射机制从接口处获取到,一般数据类型都不写-->
<select id="selectStudentById" parameterType="int" resultType="amon.domain.Student">
select * from student where id = #{id}
</select>
<!-- 多个参数,使用@Param注释-->
<select id="selectStudentsByNameAndAge" resultType="amon.domain.Student">
select * from student where name = #{name} and age = #{age}
</select>
<!-- 多个参数,使用传入对象的属性值作为参数
使用对象语法:完整应该为#{属性名,javaType=类型名称,jdbcType=类型名称}
如:#{name,javaType=java.lang.String,jdbcType=VARCHAR}
因为过于繁琐,尽量不用
直接用对象的属性名作为参数即可,java与jdbc的类型可以通过mybatis的反射机制自动获取
-->
<select id="selectStudentsByNameOrAge" resultType="amon.domain.Student">
select * from student where name = #{name} or age > #{age}
</select>
其他方法,了解就行,不建议使用
通过位置传参,
mybatis3.4版本前用#{0},#{1},表示参数位置,3.4版本以后用#{arg0}来表示
通过Map传参,
#{map中的key}
#与$的区别
'#'采用的是占位符,使用的是preparestatement对象,能够防止sql注入,效率高,用于值传递
'$'采用的是字符串拼接,使用statement对象在保证数据安全的情况下用来替换列名或者表名
设置别名
在全局配置文件mybatis.xml的下添加
通过typeAlias起别名
<!-- 自定义别名-->
<typeAliases>
<!-- type是全限定名称,alias是别名(简单易记)-->
<typeAlias type="amon.domain.Student" alias="student"></typeAlias>
</typeAliases>
之后在Dao接口的xml配置文件中,可以将resultType中的全限定名称改成自己配置好的别名
通过package起别名
<typeAliases>
<!-- 通过包名来起别名,包下的所有类名就是别名,不区分大小写-->
<typeAlias type="amon.domain.Student" alias="student"></typeAlias>
</typeAliases>
resultType返回Map
通常都是返回对象,也可以设置成返回Map,key是列名,value就是值
ps:返回时如果是Map只能是一行,相当于一个对象,如果有多行,Dao的方法返回值要定义成List’<‘Map’<‘String,Obeject’>’’>’
resultMap的使用
因为命名规则,数据库的表名与Java对象的属性名不可能完全一致,此时就不能用resultType自动分配列,需要用到resultMap,首先在mapper标签下配置一个resultMap,之后使用时将resultType替换成resultMap就可以了,不能同时使用
<!-- id:自定义名称,以后的select等标签后的resulMap里可以用此id关联
type:Java全限定名称-->
<resultMap id="studentMap" type="amon.domain.Student">
<!-- 配置数据库表列名与Java对象属性名之间的对应关系
column是列名,property是属性名-->
<!-- 主键列使用 id 标签-->
<id column="stu_id" property="stuId"></id>
<result column="stu_name" property="stuName"></result>
<result column="stu_email" property="stuEmail"></result>
<result column="stu_age" property="stuAge"></result>
</resultMap>
动态sql
可以在sql语句中使用where,if,foreach来进行语句拼接
<foreach collection="" item="" open="" close="" separator="">
#{xxx}
</foreach>
collection:表示接口中的方法参数的类型, 如果是数组使用array , 如果是list集合使用list
item:自定义的,表示数组和集合成员的变量
open:循环开始是的字符
close:循环结束时的字符
separator:集合成员之间的分隔符
定义sql语句片段
给常用的sql语句一个唯一标识,通过标识来替换,实现代码复用
主配置文件的设置
<!-- settings是 MyBatis 中全局的调整设置,它们会改变 MyBatis 的运行时行为,应谨慎设置 -->
<settings>
<!-- 该配置影响的所有映射器中配置的缓存的全局开关。默认值true -->
<setting name="cacheEnabled" value="true"/>
<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。默认值false -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 是否允许单一语句返回多结果集(需要兼容驱动)。 默认值true -->
<setting name="multipleResultSetsEnabled" value="true"/>
<!-- 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。默认值true -->
<setting name="useColumnLabel" value="true"/>
<!-- 允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。 默认值false -->
<setting name="useGeneratedKeys" value="false"/>
<!-- 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。 -->
<!-- 默认值PARTIAL -->
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<!-- 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。默认SIMPLE -->
<setting name="defaultExecutorType" value="SIMPLE"/>
<!-- 设置超时时间,它决定驱动等待数据库响应的秒数。 -->
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<!-- 允许在嵌套语句中使用分页(RowBounds)默认值False -->
<setting name="safeRowBoundsEnabled" value="false"/>
<!-- 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 默认false -->
<setting name="mapUnderscoreToCamelCase" value="false"/>
<!-- MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。
默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。
若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。 -->
<setting name="localCacheScope" value="SESSION"/>
<!-- 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 -->
<setting name="jdbcTypeForNull" value="OTHER"/>
<!-- 指定哪个对象的方法触发一次延迟加载。 -->
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
将mybatis中连接数据库的信息创建属性配置文件
之后在configuration下添加配置文件的位置信息
<properties resource="jdbc.properties">
</properties>
配置pagehelper插件
加入maven依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
在全局配置文件mybatis.xml的configuration下添加
<!--配置插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
使用插件进行分页查询
此时相当于
select * from student order by id limit ??