Mybatis入门
定义,要背诵的
- Mybatis是一款优秀的持久层框架。
- 它支持定制化SQL、存储过程以及高级映射。
- Mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
- 传统的jdbc操作 , 有很多重复代码块 .比如 : 数据取出时的封装 , 数据库的建立连接等等… , 通过框架可以减少重复代码,提高开发效率 .
- MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) —>对象关系映射
- Mybatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO(Plain Old Java Object,普通老式Java对象)为数据库中的记录。
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
- 提供xml标签,支持编写动态sql。
关键字:持久化框架,避免了JDBC代码和手动设置参数以及获取结果集,半自动化的ORM框架
xml或者注解配置和映射原生类型、接口、java的pojo为数据库中的记录
sql写在xml里,提供DAO层,业务逻辑和数据访问逻辑分离
约定大于配置:
- 开发人员仅需规定应用中不符合约定的部分
- 在没有规定配置的地方,采用默认配置,以力求最简配置为核心思想
https://blog.csdn.net/xiao_jin_ling/article/details/121062608
持久化是将程序数据在持久状态和瞬时状态间转换的机制
参考例子:
搭建一个简单的mybatis程序:https://blog.csdn.net/s001125/article/details/114676985
比较详细的例子:https://zhuanlan.zhihu.com/p/351830443
程序示例
思路流程:搭建环境—>导入Mybatis—->编写代码—->测试
搭建实验数据库
引入mybatis相关jar包
(数据库驱动mysql、mybatis、junit(单元测试))
配置mybatis配置文件
三种配置文件:jdbc的配置文件、mybatis核心配置文件、mybatis映射文件
jdbc.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///school?characterEncoding=utf-8
username=lisir
password=123456
url:数据库连接字符串(jdbc:mysql://[服务器ip地址]/数据库名 ?编码方式)
mybatis.xml
注意:mybatis全局配置需按照配置文档的顺序进行配置,也就是按照properties(属性)、settings(设置)、typeAliases(类型别名)、environments(环境配置)、mappers(映射器)的顺序进行配置,否则会报错。
<?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>
<!--引入外部配置文件-->
<properties resource="jdbc.properties"/>
<!--开启驼峰映射,setting可以不写!!-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--起别名-->
<typeAliases>
<typeAlias type="com.mybatis.pojo.User" alias="user"/>
</typeAliases>
<!--MyBatis可以配置成适应多种环境,尽管可以配置多个环境,每个SqlSessionFactory实例只能选择其一。实际使用场景下一般使用spring来管理数据源,来做到环境的分离。 -->
<environments default="development">
<!--environment表示数据源配置标签, id是配置的唯一标识-->
<environment id="development">
<!--事务管理前标签 type表示事务交给jdbc管理-->
<transactionManager type="JDBC"/>
<!--datasource表示数据源的意思,type="pooled"表示使用mybatis自带的连接池-->
<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>
<!--将xml文件映射成pojo类-->
<mappers>
<!--mapper加载映射文件的标签, resource属性表示加载映射文件资源路径-->
<!--<mapper resource="UMapper.xml"/>-->
<mapper resource="com/mybatis/dao/UserMapper.xml"/>
</mappers>
</configuration>
1、加载映射文件,关联UserMapper.java接口
2、加载接口,关联映射文件
条件:(1)接口名和映射文件名保持一致 (2)路径保持一致
mybatis映射文件
也就是mapper文件,见后续内容,主要是写增删改查等等等。
相关链接:
mybatis-config.xml MyBatis的全局配置文件
https://blog.csdn.net/weixin_43880289/article/details/109394301
讲了三个配置文件:
https://blog.csdn.net/weixin_40498531/article/details/89253662
mybatis工具类-MybatisUtils
参考的一个util,与下文列举的有些许不同:
1)在静态初始化块中加载mybatis配置文件一次
2)使用ThreadLocal对象让当前线程与SqlSession对象绑定在一起
3)获取当前线程中的SqlSession对象,如果没有的话,从SqlSessionFactory对象中获取SqlSession对象
4)获取当前线程中的SqlSession对象,再将其关闭,释放其占用的资源
总结:静态初始化加载配置文件,获取sqlSession,提交释放资源,回滚释放资源
基本功能:
//加载mybatis配置文件
//创建sqlsessionfactory对象
//创建sqlsession连接对象
//创建关闭sqlsession连接对象
参考的链接:
https://blog.csdn.net/lierenbiji21/article/details/108217738
https://www.cnblogs.com/loaderman/p/10064317.html
//sqlSessionFactory -->SqlSession
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用Mybatis第一步: 获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
//sqlSession 完全包含了面向数据库执行SQL命令所需的所有方法
public static SqlSession getSqlseesion() {
// SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSessionFactory.openSession();
}
}
创建一个实体类
user class,包含参数 id、name、pwd。
编写mapper接口
import java.util.List;
public interface UserMapper {
//查询全部用户
List<User> getUserList();
}
编写mapper.xml配置文件
<?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的接口-->
<mapper namespace="com.mybatis.dao.UserMapper">
<select id="getUserList" resultType="com.mybatis.pojo.User">
select * from mybatis.user
</select>
</mapper>
编写测试类
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlseesion();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
for(User user : userList){
System.out.println(user);
}
sqlSession.close();
}
**注意:**可能遇见资源无法导入的现象,(maven由于他的约定大于配置,可能遇到写的配置文件无法导出或生效的题),解决方案:
<!--在build中配置resource,来防止我们资源到处失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<!--将我们的资源位置加进去,用得是相对位置-->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
**namespace :**mybatis命名解析,全限定名,短限定名,如果有两个短名称,则必须使用全限定名。
<mapper namespace="com.mybatis.dao.UserMapper">
<select id="getUserList" resultType="com.mybatis.pojo.User">
select * from mybatis.user
</select>
</mapper>
增删改查
namespace中的包名要和Dao/Mapper接口的包名一致!
注意:防止sql注入
参考链接:https://www.cnblogs.com/mmzs/p/8398405.html
1、#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。
2、$将传入的数据直接显示生成在sql中。
mybatis通过预编译的方法,防止sql注入,在SQL执行前,会先将上面的SQL发送给数据库进行编译;执行时,直接使用编译好的SQL,替换占位符“?”就可以了。因为SQL注入只能对编译过程起作用,所以这样的方式就很好地避免了SQL注入的问题。
#{}是经过预编译的,是安全的;${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入
使用${},必须手动处理过滤一下输入的内容,判断输入的参数长度是否正常,更精确的过滤则可以查询一下输入参数是否在预期的参数集合中。
公共部分
xml里的参数类型:
- id : 就是对应的namespace中的方法名;
- parameterType : 参数类型!
- resultType:Sql语句执行的返回值!
编写接口
写一个mapper,mapper声明一些函数,getXXXX,addXXXX,updateXXX,deleteXXX。
select、insert、update、delete
测试函数
@Test
public void getUserById() {
//获得sqlsession
SqlSession sqlSession = MybatisUtils.getSqlSession();
//映射mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//使用mapper的查询函数
User user = mapper.getUserById(1);
System.out.println(user);
//关闭sql
sqlSession.close();
}
增
增加,不需要返回值,只需要传入值。
<!--对象中的属性,可以直接取出来-->
<insert id="addUser" parameterType="com.kuang.pojo.User">
insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd});
</insert>
删
删除带有条件
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id = #{id};
</delete>
改
<update id="updateUser" parameterType="com.kuang.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd} where id = #{id} ;
</update>
查
<select id="getUserById" parameterType="int" resultType="com.kuang.pojo.User">
select * from mybatis.user where id = #{id}
</select>
**注意:**增删改需要提交事务,事务分为手动提交和自动提交。
sqlSession.commit();
万能map
参考:https://blog.csdn.net/chengqiuming/article/details/106790938
如果我们的实体类或者数据库中的表,字段或参数过多,可以考虑使用Map。
Map传递参数,直接在sql中取出key即可,根据key取出元素。
int addUser2(Map<String,Object> map);
xml中:
<!--对象中的属性,可以直接取出来 传递map的key-->
<insert id="addUser" parameterType="map">
insert into mybatis.user (id, pwd) values (#{userid},#{passWord});
</insert>
使用
@Test
public void addUser2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("userid",5);
map.put("passWord","2222333");
mapper.addUser2(map);
sqlSession.close();
}
多个参数用Map,或者注解!
模糊查询
1、java代码执行的时候,传递通配符%
List<User> userList = userDao.getUserLike("胡%");
2、在sql拼接中使用通配符
select * from mybatis.user where name like "%"#{value}"%"
作用域scope和生命周期
不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
//sqlSessionFactory -->SqlSession
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用Mybatis第一步: 获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
//sqlSession 完全包含了面向数据库执行SQL命令所需的所有方法
public static SqlSession getSqlseesion() {
// SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSessionFactory.openSession();
}
}
SqlSessionFactoryBuilder
可以被实例化、使用和丢弃,一旦创建SqlSessionFactory,就不再需要它。因此它的最佳作用域是方法作用域(局部方法变量)。可以重用SqlSessionFactoryBuilder创建多个SqlSessionFactory实例,但最好不要一直保留它,以保证所有的xml解析资源可以被释放给更重要的事情。
- 一旦创建 SqlSessionFactory,就不再需要它了
- 局部变量
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSessionFactory
- 可以想象为:数据库连接池
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或者重新创建另一个实例
- 因此 SqlSessionFactory 的最佳作用域是应用作用域
- 最简单的就是使用单例模式或者静态单例模式
所以经常被声明为静态变量,只被创建一次。
private static SqlSessionFactory sqlSessionFactory;
SqlSession
- 连接到连接池的一个请求!
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
- 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。
- 用完之后需要赶紧关闭,否则资源被占用!
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}
如下所示,用完就close(),一般可以卸载finally块中。
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//你的应用逻辑代码
sqlSession.close();
映射器实例
- 映射器接口的实例是从 SqlSession 中获得的。
- 映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃。 映射器实例并不需要被显式地关闭。
- 最好将映射器放在方法作用域内
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
// 你的应用逻辑代码
}
或者如上面的例子
public void getUserById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
//映射mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//。。。。。相关逻辑
//关闭sql
sqlSession.close();
}
mybatis的流程
![img](https://i-blog.csdnimg.cn/blog_migrate/e51fd05c618ed197344c0a0ed6d31db0.jpeg)
解决属性名和字段名不一样
数据库中字段:id、name、pwd
实体类中字段:id、name、password
1、sql语句改一下,起别名
<select id="getUserById" resultType="com.mybatis.pojo.User">
select id,name,pwd as password from mybatis.user where id =#{id}
</select>
2、对结果集进行一下映射 resultmap
<!--结果集映射-->
<resultMap id="UserMap" type="User">
<!--column数据库中的字段,property实体类中的属性-->
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserByid" resultMap="UserMap">
select * from mybatis.user where id =#{id}
<select>
分页
为什么要使用分页,减少数据的处理量。
使用limit分页
startIndex:从第几行开始 pageSize:取几条数据
select * from user limit startIndex, pageSize
select * from user limit 0,2
使用mybatis实现分页
1、接口
List<User> getUserByLimit(Map<String,Integer> map);
2、mapper.xml
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize};
</select>
3、测试
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",1); //重点!!
map.put("pageSize",2);
List<User> userByLimit = mapper.getUserByLimit(map);
for(User user:userByLimit){
System.out.println(user);
}
sqlSession.close();
}
RowBounds分页
1、接口
List<User> getUserByRowBounds();
2、mapper.xml
<select id="getUserByRowBounds" resultMap="UserMap">
select * from muser
</select>
3、测试
@Test
public void getUserByRowsBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
RowBounds rowBounds = new RowBounds(1, 2); //设置rowBounds对象,起始条,查询条数
List<User> userList = sqlSession.selectList("com.susu.dao.UserMapper.getUserByRowBounds",null,rowBounds);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
插件实现
pageHelper
具体教程:
https://pagehelper.github.io/docs/howtouse/
详细讲述分页:
具体参考环境:springboot、mybatis、pagehelper
使用步骤:
1、pom.xml引入依赖库
2、application.yml或者application.properties中添加分页配置
3、进行使用,在controller层或者service层使用
https://blog.csdn.net/Cs_hnu_scw/article/details/80718467
使用注解开发
面向接口编程,主要考虑对象之间的协作关系的设计。
面向对象:以对象为单位,考虑它的属性以及方法。
面向过程:以一个具体的流程(事务过程)为单位,考虑它的实现。
1、注解在接口上实现
这个文件是UserMapper.java
@Select("select * from muser")
List<User> getUsers();
2、需要在核心配置文件中绑定接口
<mappers>
<mapper class="com.mybatis.dao.UserMapper"/>
</mappers>
3、测试
和上面的代码,步骤一样,getsqlsession,mapper.class,调用getUsers方法。
注解CRUD
1、mybatis util工具类创建的时候实现事务自动提交
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
2、编写接口,增加注解
注解多个参数,用@Param(推荐),也可以使用map
https://blog.csdn.net/weixin_37139197/article/details/82975594
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
@Select("select * from user where id = #{id}")
User getUserById(@Param("id") int id);
//方法存在多个参数,所有的参数前面必须加上@param("xxx")
@Select("select * from user where id = #{id} and name = #{name}")
User getUserById(@Param("id") int id,@Param("name") string name);
//增加
@Insert("insert into user(id,name,pwd) values(#{id},#{name},#{paswword})")
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);
}
配置接口,在mybatis的核心配置文件中写上。
<mappers>
<mapper class="com.mybatis.dao.UserMapper"/>
</mappers>
注意:@param注解
- 基本类型的参数或者string类型,需要加上
- 引用类型不需要加,(传递一个对象啥的)
- 如果只有一个基本类型的话,可以忽略,但是建议加上!
- 在sql中引用的就是这里的@param() 中的属性名!