概述
MyBbatis它是一款半自动的ORM持久层框架,具有较高的SQL灵活性,支持高级映射(一对一,一对多),动态SQL,延迟加载和缓存等特性,但它的数据库无关性较低。
ORM是什么?
Object Relation Mapping,对象关系映射。对象指的是Java对象,关系指的是数据库中的关系模型,对象关系映射,指的就是在Java对象和数据库的关系模型之间建立一种对应关系,比如用一个Java的User类,去对应数据库中的一张user表,类中的属性和表中的列一一对应。
mybatis为什么是半自动的ORM框架?
以为使用mybatis开发的时候,需要手写sql语句,而其他全自动的ORM框架,则不需要手写sql语句。更因为mybatis需要手写sql语句,拥有更好的灵活性。与传统的JDBC相比,mybatis提供了输入和输出映射,可以很方便地进行SQL设置,以及结果集的封装。并且还提供了关联查询和动态SQL等功能,极大地提升了开发的效率。
快速开发
步骤
- 编写全局配置文件
- 编写mapper映射文件
- 加载全局配置文件,生成SqlSessionFactory
- 创建SqlSession,调用mapper映射文件中的SQL语句来执行CRUD操作
具体操作
1. 在IDEA中新建一个maven的项目
2. 在项目的porn.xml配置中导入相关依赖,例:
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
3. 创建mybatis-config.xml全局配置文件
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"/>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.weirdo.dao.AuthorMapper"/>
</mappers>
</configuration>
4. 配置数据库连接文件db.properties
driver = com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&userUnicode=true&acharacterEncoding=UTF-8
username=root
password=root
5. 编写Utils工具类
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(true);
}
}
7. 创建实体类
public class User {
private int id;
private String name;
private String pwd;
}
8. 编写mapper映射文件(Mapper.xml)
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dao.UserMapper">
<select id="getUserLike" resultType="com.pojo.User">
select * from mybatis.user where name like #{value}
</select>
<select id="getUserList" resultType="com.pojo.User">
select * from mybatis.user
</select>
<select id="getUserById" parameterType="int" resultType="com.pojo.User">
select * from mybatis.user where id = #{id}
</select>
<insert id="addUser" parameterType="com.pojo.User">
insert into mybatis.user (id,name,pwd) value (#{id},#{name},#{pwd})
</insert>
<update id="updateUser" parameterType="com.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd} where id =#{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id=#{id}
</delete>
</mapper>
9. 编写测试类
public class UserDaoTest {
@Test
public void getUserLike(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserLike("%李%");
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void test(){
SqlSession sqlSession= MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}
@Test
public void addUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.addUser(new User(9, "小李", "1233"));
if(res>0){
System.out.println("插入成功");
}
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.updateUser(new User(2,"小王","1115"));
sqlSession.commit();
sqlSession.close();
}
@Test
public void deleteUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUser(5);
sqlSession.commit()
sqlSession.close();
}
}
一些注意问题(和解决方法)
- 在项目中导入相关依赖后,仍然报错,则需要刷新maven仓库中的项目依赖
- 全局配置文件中的标签是有顺序的
<configuration>
</configuration>
- mappers标签引入映射器的俩种方式(常用)
<mappers>
<mapper resource="com/weirdo/dao/AuthorMapper.xml"/>
</mappers>
<mappers>
<mapper class="com.weirdo.dao.AuthorMapper"/>
</mappers>
- mapper.xml中增删改需要提交事务,在测试类中添加
sqlSession.commit();
也可以在工具类中设置(默认为flase,改为true即为自动提交)
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(true);
}
- mapper.xml的SQL语句中的占位符 ${ } 和 #{ }
一般会采用#{},#{}输入参数的占位符,相当于jdbc的?,防注入 ,自动添加了‘ ’ 引号! 而${},一般会用在模糊查询的情景,比如
SELECT * FROM student WHERE name like '%${name}%';
- 关于resultType和parameterType区别
resultType:返回值类型,类型即为对象类型,返回结果字段与对象属性匹配映射,类型中的属性名要与查询条件保持一致,否则就会失败。 parameterType:接收参数类型,同为对象类型,单字段 例如String类型 可以使用全包名或者单名称。
基于XML代理开发
全局配置文件和mapper.xml文件是最基本的配置,在完成基本配置后,我们可以不需要创建dao类,而可以直接创建一个mapper接口,进行处理。
- 创建接口
public interface UserMapper {
List<User> getUserList();
User getUserById(int id);
int addUser(User user);
int updateUser(User user);
int deleteUser(int id);
}
- 创建mapper.xml
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dao.UserMapper">
<select id="getUserList" resultType="User">
select * from mybatis.user
</select>
<select id="getUserById" parameterType="int" resultType="com.pojo.User">
select * from mybatis.user where id = #{id}
</select>
<insert id="addUser" parameterType="com.pojo.User">
insert into mybatis.user (id,name,pwd) value (#{id},#{name},#{pwd})
</insert>
<update id="updateUser" parameterType="com.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd} where id =#{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id=#{id}
</delete>
</mapper>
- 测试类
public class UserDaoTest {
@Test
public void test(){
SqlSession sqlSession= MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}
@Test
public void addUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.addUser(new User(9, "小李", "1233"));
if(res>0){
System.out.println("插入成功");
}
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.updateUser(new User(2,"小王","1115"));
sqlSession.commit();
sqlSession.close();
}
@Test
public void deleteUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUser(5);
sqlSession.commit();
sqlSession.close();
}
}
基于xml代理开发的注意事项
mapper接口和mapper.xml之间需要遵循一定规则,才能成功的让mybatis将mapper接口和mapper.xml绑定起来
- mapper接口的全限定名,要和mapper.xml的namespace属性一致
- mapper接口中的方法名要和mapper.xml中的SQL标签的id一致
- mapper接口中的方法入参类型,要和mapper.xml中SQL语句的入参类型一致
- mapper接口中的方法出参类型,要和mapper.xml中SQL语句的返回值类型一致
基于注解代理开发
如果对xml配置文件感到麻烦,则可以考虑使用注解的开发方式,不过注解的开发方式,会将SQL语句写到代码文件中,后续的维护性和扩展性不是很好(如果想修改SQL语句,就得改代码,得重新打包部署,而如果用xml方式,则只需要修改xml,用新的xml取替换旧的xml即可)。
使用注解的开发方式,也还是得有一个全局配置的xml文件,不过mapper.xml就可以省掉了,具体操作只用2步,如下
- 创建一个mapper接口
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
@Select("select * from user where id = #{id}")
User getUserById(@Param("id") int id);
@Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
int addUser(User user);
@Update("update user set name=#{name},pwd=#{password} where id = #{id}")
int updateUser(User user);
@Delete("delete from user where id=#{id} ")
int deleteUser(@Param("id") int id);
}
- 在全局配置文件中修改mappers标签,指定到mapper接口处
<mappers>
<mapper class="com.weirdo.dao.UserMapper"/>
</mappers>
- 测试类
public class UserMapperTest {
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUser(5);
sqlSession.close();
}
}
基于注解代理开发的注意事项
- 当使用注解开发时,若需要传入多个参数,可以结合
@Param 注解 @Param 标签会被mybatis处理并封装成一个Map对象,比如上面的示例中,实际传入的参数是一个Map对象,@Param 标签帮忙向Map中设置了值- 在文章的上面,介绍了俩种mappers标签映入映射器的方式,在此处做一个细致的探讨补充
<mappers>
<mapper resource="com/weirdo/dao/AuthorMapper.xml"/>
</mappers>
<mappers>
<mapper class="com.weirdo.dao.AuthorMapper"/>
</mappers>
而在实际工作中,一般我们会将一张表的SQL操作封装在一个mapper.xml中,可能有许多张表需要操作,那么我们是不是要在<mappers>标签下写多个<mapper>标签呢?其实不用,还有第三种加载mapper的方法,使用<package> 标签
<mappers>
<package name="com.weirdo.mapper"/>
</mappers>
这样就会自动加载com.weirdo.mapper 包下的所有mapper,这种方式需要将mapper接口文件和mapper.xml文件都放在com.weirdo.mapper 包下,且接口文件和xml文件的文件名要一致。
- 关于target工程目录下需要添加以下配置
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
mappers标签里映射的时候会出现找不到mapper的情况。这是因为src/main/java 源码目录下的文件,maven打包时只会将该目录下的java文件打包,而其他类型的文件都不会被打包进去。需要在porn.xml 的添加相关配置才可以。
动态SQL
可以根据具体的参数条件,来对SQL语句进行动态拼接。
比如在开发中,由于不确定查询参数是否存在,会使用类似于where 1 = 1 来作为前缀,然后后面用AND 拼接要查询的参数,这样,就算要查询的参数为空,也能够正确执行查询,如果不加1 = 1 ,则如果查询参数为空,SQL语句就会变成SELECT * FROM student where ,SQL不合法。
mybatis里的动态标签主要有
<select id="queryBlogIF" parameterType="map" resultType="blog">
select * from mybatis.blog where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</select>
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views=#{views}
</otherwise>
</choose>
</where>
</select>
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id}
</update>
SQL片段
有些时候可以用sql便签抽取公共部分,在使用的地方使用include标签
要注意:
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<include refid="if-title-author"></include>
</where>
</select>
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="and ("
close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
测试类
@Test
public void queryBlogForeach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
ArrayList<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(3);
map.put("ids",ids);
List<Blog> list = blogMapper.queryBlogForeach(map);
for (Blog blog : list) {
System.out.println(blog);
}
sqlSession.close();
}
关于缓存
一级缓存
默认开启状态,同一个SqlSession级别共享的缓存,在一个SqlSession的生命周期内,执行俩次SQL查询,当然第二次查询会直接查询缓存,而不是数据库。 但如果,第一次和第二次相同的SQL查询之间,执行了INSERT/UPDATE/DELETE 这种DML操作,一级缓存会被清空,第二次会查询数据库
缓存失效
- 同一个SqlSession下执行增删改操作,不用提交,会清空一级缓存
- SqlSession提交或关闭的时候,会清除一级缓存
- 在全局配置文件中设置
<setting name="localCacheScope" value="STATEMENT"/> 这样会使一级缓存失效,二级缓存并不受影响 - 对mapper.xml中的某个CRUD标签,设置属性
flushCache=true ,这样会导致该MappedStatement的一级缓存,二级缓存都失效(一个CRUD标签在mybatis中会被封装成一个MappedStatement
二级缓存
二级缓存是基于namespace和mapper的作用域起作用的,不是依赖于SQL session,默认关闭,在mybatis-config.xml文件中的标签配置开启缓存<settings name="cacheEnabled" value="true"/> 开启二级缓存总开关,然后在某个具体的mapper.xml中增加<cache /> ,即开启了该mapper.xml的二级缓存。二级缓存是mapper级别的缓存,粒度比一级缓存大,多个SqlSession可以共享同一个mapper的二级缓存。注意开启二级缓存后,SqlSession需要提交,查询的数据才会被刷新到二级缓存当中
association和collection的区别
简单理解就是,association是用于一对一和多对一关系 collection是用于一对多关系
|