Mybatis 教程1
文章目录
mybatis的简介
前身叫Ibatis,是一个&半自动的ORM框架(Object R model)对象关系模型&,完整ORM框架(hibernate),hibernate的入门难度高,mybatis的入门级别低,如果你会用jdbc,你应该会用mybatis,仅仅是对jdbc的封装
MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了[google code](https://baike.baidu.com/item/google code/2346604?fromModule=lemma_inlink),并且改名为MyBatis。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)。
当前,最新版本是MyBatis 3.5.13,其发布时间是2023年03月11日。
迅速在中国互联网行业应用(hibernate>mybatis),原因:简单,中国软件从业人员的能力
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。 [1]
传统JDBC存在的问题
- 数据库连接信息硬编码(配置文件)【全局配置文件】
Class.forName("com.mysql.cj.jdbc.Driver") // crac
DriverManager.getConnection(url,username,pwd);// 开发环境username root修改
// 保存源码 编译字节码--生产环境和开发环境完全一致
// 开发环境 装有eclipse的环境 jdk a
// 生产环境 服务器上的环境 crm
// 测试环境 用于测试人员进行测试的环境 crm
// 环境:操作系统的位数 JDK的版本号
// 1.8 jdk 包含有xml操作的依赖
// 11 jdk 将xml的依赖移除了
- 频繁获取连接(数据库连接池)【mybatis自主开发的数据库连接池】
- 查询结果封装(使用反射进行封装)【通过xml配置来解决】
- Sql语句应编码问题(可以通过配置文件来解决)【映射文件】
mybatis帮我们解决了以上所有问题
mybatis的运行原理
- mybatis的核心是配置文件,分别为全局配置文件(1个)和映射文件(n个)。全局配置文件中定义了数据库连接信息、事务信息、数据库连接池信息、别名、插件、加载映射文件等信息。映射文件中包含sql语句以及结果映射、动态sql等。
- SqlSessionFactoryBuild加载配置文件生成SqlSessionFactory(构建者模式)
- SqlSessionFactory.openSession的方法来获取SqlSession对象(工厂模式)
- SqlSession是一个接口,他是mybatis提供给开发人员的开发标准(用于操作数据库的所有的方法),包含有insert update delete等操作数据库的方法
- SqlSession调用exector执行器(SimpleExector,CacheExector,BatchExector),让执行器来操作数据库
- 执行器调用mapperstatement对象,包含sql语句,输入参数,输出结果
- 真实的调用数据库
xml
xml是一种重量级数据传输格式,一般使用在EJB
- 声明,声明xml的编码格式以及版本信息
<?xml version="1.0" encoding="UTF-8"?>
-
约束(用来规定xml中节点的名称以及节点中的属性的名称的书写规范的)
- dtd约束
- schema约束
-
自定义节点信息
如果xml中包含有约束,自定义节点必须按照约束的书写规范进行编写,如果没有约束,随便写
搭建入门案例
- 创建se的工程
- 添加依赖
- mysql-connector-java
- mybatis依赖
https://mvnrepository.com/
- 创建全局配置文件xml
<?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">
<configuration>
<!-- 环境(多环境定义)
default:复数环境中定义的单个环境的id,
-->
<environments default="dev">
<!-- 单个环境定义
id: 当前环境的唯一编号
-->
<environment id="dev">
<!-- 定义事务
type : jdbc使用jdbc的事务
MANAGED 使用非事务的形式
-->
<transactionManager type="JDBC"/>
<!--
数据源 数据库连接池的定义
type: POOLED 连接池(mybatis自己开发的连接池,性能差)
不允许使用第三方连接池
UNPOOLED 使用非连接池的方式
JNDI (java naming directory interface)
java命名目录接口
底层架构使用JNDI
-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///db1"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
<environment id="prod">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///java2301q"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 加载映射文件 -->
<mappers>
<!-- resource代表classpath下xml的路径 -->
<mapper resource="user.xml"/>
</mappers>
</configuration>
- 创建映射文件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 namespace="user">
<!-- 如果封装结果是集合,resultType中只需要定义集合中泛型 -->
<select id="selectUsers" resultType="com.xx.pojo.User">
select * from sys_user
</select>
</mapper>
- 使用构建者模式创建sqlSessionFactory
package com.xx.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
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 com.xx.pojo.User;
public class Test1 {
public static void main(String[] args) throws IOException {
SqlSessionFactoryBuilder build = new SqlSessionFactoryBuilder();
// 需要将配置文件转为inputStream
// 将classpath下的文件转为inputstream的方法
InputStream inputStream = Resources.getResourceAsStream("configer.xml");
SqlSessionFactory factory = build.build(inputStream);
SqlSession session = factory.openSession();
// 放mapperstatement的id,一部分内容 namespace.sqlId
List<User> users = session.selectList("user.selectUsers");
users.stream().forEach(a->{
System.out.println(a.getName());
});
session.close();
}
}
Dao模式
分析作用域
SqlSessionFactoryBuilder
确定sqlSessionFactoryBuilder是单例
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。
SqlSessionFactory(单例)
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。注意:如果factory是多例,缓存没有效果
SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中
sqlSession是线程不安全的,不允许作为成员变量,应该在每个方法中创建独立的对象
是IBATIS的实现方式,结合DAO模式进行数据库操作
Dao模式中包含的内容:
- 实体类
- DAO接口
package com.xx.dao;
import java.util.List;
import com.xxx.pojo.User;
public interface UserDao {
/**
* insert用户信息
* @param user 用户信息
* @return 影响行数
*/
int insertUser(User user);
int updateUser(User user);
int deleteById(Integer id);
User selectById(Integer id);
List<User> selectUsers();
}
- 接口的实现类
- 工具类
package com.xx![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/da91de2d050341bdb0bcfb1eadc17b3b.png#pic_center)
.util;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class SqlSessionUtil {
private static SqlSessionFactoryBuilder builder;
private static SqlSessionFactory factory;
static {
try {
builder = new SqlSessionFactoryBuilder();
InputStream inputStream = Resources.getResourceAsStream("configer.xml");
factory = builder.build(inputStream);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static SqlSession getSession() {
SqlSession session = factory.openSession();
return session;
}
}
SqlSession API
openSession()默认采用了手动提交的事务,openSession(true)可以修改为自动提交的事务,也可以使用session.commit()手动提交事务
方法名 | 返回结果 | 描述 |
---|---|---|
int insert(String statement); | int | 只能执行insert语句,返回结果为影响行数。执行的是静态sql |
int insert(String statement, Object parameter); | int | 只能执行insert语句,返回结果为影响行数。执行的是预编译sql,参数2为占位符需要的数据 |
commit() | 手动提交事务 | |
int update(String statement) | int | 只能执行update语句,返回结果为影响行数。执行的是静态sql |
int update(String statement,Object parameter) | int | 只能执行update语句,返回结果为影响行数。执行的是预编译sql,参数2为占位符需要的数据 |
int delete(String statmenet) | int | 只能执行delete语句,返回结果为影响行数。执行的是静态sql |
int delete(String statement,Object paramter) | int | 只能执行delete语句,返回结果为影响行数。执行的是预编译sql,参数2为占位符需要的数据 |
T selectOne(String statement); | 任意类型 | 执行的查询语句查询结果<=1条的,只能执行DQL语句 |
T selectOne(String statement,Object paramter); | 任意类型 | 执行的查询语句动态sql查询结果<=1条的,只能执行DQL语句 |
List selectList(String statement,Object paramter); | 集合 | 执行的动态sql,查询结果>=0 |
List selectList(String statement); | 集合 | 执行静态sql |
rollback | 事务回滚 | |
getConfiguration(); | Configuration | 获取到全局配置。返回结果是将全局配置文件封装成的对象 |
T getMapper(Class type); | 任意类型 | 代理模式中用来获取接口的代理对象的方法 |
注意
mybatis中的占位符不能使用?,采用#{}
#{}要求
- 如果输入参数是简单数据类型#{任意值}
#{sddfsdfsdfsdfsd}
- 如果输入参数是自定义类型#{属性的属性名}
- 如果输入参数是map类型#{key}
${}字符拼接,要求
- 如果输入参数是简单数据类型${value}
- 如果输入参数是自定义类型${属性的属性名}
- 如果输入参数是map类型${key}
resultType:
同名的结果映射,如果查询结果的列名和实体类的属性名完全不一致,返回null,如果查询结果的列名有和实体类的属性名一致的,返回结果不为空,同名的封装成功,不同名,结果封装不成功采用属性的默认值。
package com.xxx.dao.impl;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.xxx.dao.UserDao;
import com.xxx.pojo.User;
import com.xxx.util.SqlSessionUtil;
public class UserDaoImpl implements UserDao {
@Override
public int insertUser(User user) {
SqlSession session = SqlSessionUtil.getSession();
int flag=0;
try {
flag = session.insert("user.insertUser",user);
int i =1/user.getAge();
session.commit();
}catch(Exception ex) {
ex.printStackTrace();
session.rollback();
}
session.close();
return flag;
}
@Override
public int updateUser(User user) {
SqlSession session = SqlSessionUtil.getSession();
int flag = session.insert("user.updateUser",user);
session.close();
return flag;
}
@Override
public int deleteById(Integer id) {
SqlSession session = SqlSessionUtil.getSession();
int flag = session.insert("user.deleteById",id);
session.close();
return flag;
}
@Override
public User selectById(Integer id) {
SqlSession session = SqlSessionUtil.getSession();
User user = session.selectOne("user.selectUserById",id);
session.close();
return user;
}
@Override
public List<User> selectUsers() {
SqlSession session = SqlSessionUtil.getSession();
List<User> users = session.selectList("user.selectUsers");
session.close();
return users;
}
}
<?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 namespace="user">
<!-- 如果封装结果是集合,
resultType中只需要定义集合中泛型
如果调用selectList,resultType代表的是集合中的泛型
-->
<select id="selectUsers" resultType="com.xxx.pojo.User">
select * from tb_user
</select>
<insert id="insertUser" parameterType="com.xxx.pojo.User">
insert into tb_user(name,sex,age) values('${name}',${sex},${age})
</insert>
<update id="updateUser">
update tb_user set name=#{name},sex=#{sex},age=#{age}
where id=#{id}
</update>
<delete id="deleteById">
delete from tb_user where id=#{id}
</delete>
<!--
mapperstatemet
resultType:用于结果映射,进行的是同名的结果映射
-->
<select id="selectUserById" resultType="com.xxx.pojo.User">
select id id1,name name2,sex sex2,age age1 from tb_user
</select>
</mapper>