MyBatis学习总结(超级详细哦,尤其适合小白)
Posted 我实在是想不出什么好听的昵称了啊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis学习总结(超级详细哦,尤其适合小白)相关的知识,希望对你有一定的参考价值。
什么是MyBatis?
- MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- MyBatis 本是apache的一个开源项目ibatis, 2010年这个项目由apache 迁移到了google code,并且改名为MyBatis 。
- 附MyBatis官方文档 : http://www.mybatis.org/mybatis-3/zh/index.html
MyBatis入门程序
-
MyBatis相关Jar包:(使用Maven构建)`
<groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency>`
-
mysql数据库驱动包:`
<groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency>`
-
Junit单元测试包:`
<groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>`
-
XML核心配置文件(mybatis-config.xml)构建SqlSessionFactory:
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybaits?serverTimezone=UTC&allowPublicKeyRetrieval=true&useSSL=false&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
环境搭建:
public class User {
private int id;
private String last_name;
private String email;
public User() {
}
public User(int id, String last_name, String email) {
this.id = id;
this.last_name = last_name;
this.email = email;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLast_name() {
return last_name;
}
public void setLast_name(String last_name) {
this.last_name = last_name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", last_name='" + last_name + '\\'' +
", email='" + email + '\\'' +
'}';
}
}
Tips:
平时为了避免不必要的麻烦,可以在设置实体类属性的时候,尽量保证其跟数据库字段名一致,当数据库字段名有下划线时候,实体类属性名可以采用驼峰命名规则。但是具体还是看要求,约定大于配置!!!后面也会有相关属性名跟字段名不一致的解决方案。
还有就是,最好自动写上get,set方法以及构造器和tostring方法。
public interface UserMapper {
//查询所有用户
List<User> getUserList();
}
<?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">
<!--namespace==绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.xxx.dao.UserMapper">
<!--select查询所有用户语句-->
<select id="getUserList" resultType="com.xxx.pojo.User">
/*执行sql*/
select * from user
</select>
</mapper>
-
单元测试:
- 每个基于MyBatis 的应用都是以一个 SqlSessionFactory的实例为核心的,SqlSessionFactory 的实例可以通过SqlSessionFactoryBuilder 获得。
- SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的Configuration 实例来构建出SqlSessionFactory 实例。
编写一个MyBatis工具类获取sqlSession对象
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
private static InputStream inputStream;
static {
try {
//1.使用MyBatis获取SqlSessionFactory 实例
String resource = "mybatis-config.xml";
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
@Test
public void getUserList() {
//1:获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式1:getMapper,执行sql
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
//3:关闭sqlSession
sqlSession.close();
}
以上是使用xml文件从SqlSessionFactory 中获取 SqlSession,使用和指定语句的参数和返回值相匹配的接口(比如 BlogMapper.class),现在你的代码不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换。
还有一种方式是:可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:(不推荐使用)
try (SqlSession session = sqlSessionFactory.openSession()) {
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}
注意点和遇到的问题
<!--每一个mapper.xml都需要在Mybatis核心配置文件中配置-->
<mappers>
<mapper resource="com/xxx/dao/UserMapper.xml"/>
</mappers>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<argLine>
-Dfile.encoding=UTF-8
</argLine>
</configuration>
</plugin>
</plugins>
-
添加后还是有如下问题:这个问题我上网找了很多资料,都是说这是编码为UTF-8形式即可,但是还是一直报错,最后我自己捣鼓试了一下将UTF-8修改为UTF8,居然可以了,真是夺笋啊!!!!而且,是要将项目设计的两个UserMapper,xml和mybatis-config.xml都修改才行,如下所示:
<?xml version="1.0" encoding="UTF8" ?>
相信很多人都出现过如下情况:
org.apache.ibatis.binding.BindingException: Type interface com.xxx.dao.UserMapper is not known to the MapperRegistry.
表示未绑定注册mapper,有可能是idea无法扫描到所需的xml资源,解决方法如下:在maven的pom文件中的标签内加上:
<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>
即可扫描到src下的java和resources下的所有properties和xml资源。
MyBatis常用XML配置
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- mappers(映射器)
注意:
configuration" 里的标签顺序如下:(否则报错如下信息)
"(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?
objectWrapperFactory?,reflectorFactory?,plugins?,environments?,
databaseIdProvider?,mappers?)"
具体还是建议查看文档吧,这里比较齐全:
MyBatis的CRUD操作示例
public interface UserMapper {
//查询所有用户
List<User> getUserList();
//根据id查询用户
User getUserById(int id);
//添加一个用户
int addUser(User user);
//修改用户
int updateUser(User user);
//删除一个用户
int deleteUser(int id);
}
<?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">
<!--namespace==绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.xxx.dao.UserMapper">
<!--select查询所有用户语句-->
<select id="getUserList" resultType="com.xxx.pojo.User">
/*执行sql*/
select * from user
</select>
<!--根据id查询用户-->
<select id="getUserById" resultType="com.xxx.pojo.User" parameterType="_int">
select * from mybaits.user where id =#{id}
</select>
<!--添加一个用户-->
<insert id="addUser" parameterType="com.xxx.pojo.User" >
insert into mybaits.user(id,last_name,email) values (#{id},#{last_name},#{email})
</insert>
<!--修改用户-->
<update id="updateUser" parameterType="com.xxx.pojo.User">
update mybaits.user
set last_name =#{last_name},email=#{email}
where id =#{id};
</update>
<!--删除用户-->
<delete id="deleteUser" parameterType="com.xxx.pojo.User">/*这里写int也可以*/
delete from mybaits.user where id =#{id}
</delete>
</mapper>
以上第二个select这个语句名为:getUserById,接受一个 int(或 Integer)类型的参数,并返回一个User类型的对象,其中的键是列名,值便是结果行中的对应值。其中,参数形式为:#{}
id 在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器
resultType 期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
resultMap 对外部 resultMap 的命名引用。结果映射是
flushCache 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache 将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
resultOrdered 这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false。
resultSets 这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。
Update,Insert,Delete常用属性跟Select差不多,只是个别不同,用到时候再记录。
public class UserMapperTest {
@Test
public void getUserList() {
//1:获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式1:getMapper,执行sql
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
//3:关闭sqlSession
sqlSession.close();
}
@Test
public void getUserById() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User userById = mapper.getUserById(2);
System.out.println(userById);
sqlSession.close();
}
@Test
public void addUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int addUser = mapper.addUser(new User(4, "赵丽颖", "com@zhao"));
if(addUser>0){
System.out.println("插入成功!");
}
//一定要提交事务,否则及时输出插入成功,数据库也不会有数据
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int updateUser = mapper.updateUser(new User(3, "张艺兴", "com@zhang"));
if(updateUser>0){
System.out.println("修改成功!");
}
//一定要提交事务,否则及时输出插入成功,数据库也不会有数据
sqlSession.commit();
sqlSession.close();
}
@Test
public void deleteUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int deleteUser = mapper.deleteUser(2);
if(deleteUser>0){
System.out.println("删除成功!");
}else {
System.out.println("删除失败!");
}
//一定要提交事务,否则即使输出插入成功,数据库也不会有数据
sqlSession.commit();
sqlSession.close();
}
}
解决数据库表字段名跟属性名不一致问题:(例如:字段名为last_name,属性名为lastName,若使用resultType,查询出来lastName为NULL)
- lastName为NULL的原因为:MyBatis的自动映射机制会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) ,
去对应的实体类中查找相应列名的set方法设值 , 由于找不到setlastName() , 所以lastName返回null. - 解决方法:
方法一:为列名指定别名 , 别名和java实体类的属性名一致 。
<select id="getUserList" resultType="com.xxx.pojo.User">
select id , last_name as lastName , eamil from user where id = #{id}
</select>
方法二:使用结果集映射->ResultMap 【推荐】
<select id="getUserList" resultType="com.xxx.pojo.User" resultMap="userMap">
/*执行sql*/
select * from user
</select>
<resultMap id="getUserList" type="com.xxx.pojo.User">
<result column="last_name" property="name"></result>
</resultMap>
(注意:除了select操作,其他三种都要提交事务,否则不会报错,但是会执行失败!)
作用域(Scope)和生命周期
理解我们目前已经讨论过的不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。
我们可以先画一个流程图,分析一下Mybatis的执行过程!
-
作用域理解
- SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)
- SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期
- 由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况
- 因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域
以上是关于MyBatis学习总结(超级详细哦,尤其适合小白)的主要内容,如果未能解决你的问题,请参考以下文章