MyBatis
一、初识
二、执行流程
三、CRUD
SELECT 查询
id:对应Mapper文件中的方法
resultType:返回值类型
paremeterType:参数类型
#{ }占位符
<select id="getUserById" resultType="com.kk.po.User" parameterType="Integer">
select * from t_user where id=#{id}
</select>
INSERT 添加 UPDATE 更新 DELETE删除
一定要提交事务 sqlSession.commit()
<insert id="addUser" parameterType="com.kk.po.User">
insert into t_user(id,name,age) values(#{id},#{name},#{age})
</insert>
<update id="updateUser" parameterType="com.kk.po.User">
update t_user set name= #{name},age=#{age} where id=#{id}
</update>
<delete id="deleteUser" parameterType="String">
delete from t_user where name=#{name}
</delete>
四、常见报错信息提示
-
xml文件中文注释报错 【com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 2 字节的 UTF-8 序列的字节 2 无效】
xml文件编码方式和IDEA编码方式不一致
- mapper文件未加载【org.apache.ibatis.binding.BindingException: Type interface com.kk.dao.UserDao is not known to the MapperRegistry】
<!-- 在mybatis-config中 mapper配置-->
<mappers>
<mapper resource="com/kk/dao/UserDao.xml"/>
</mappers>
五、万能Map(非正规)
假设我们的实体类、或者数据库中的表,字段或参数过多,我们应当考虑使用Map
#{}内的字段名可以随意取名,不必与数据库字段名对应
Map传递参数,直接在sql中取出key即可
多个参数用Map或注解
<update id="updateUserByMap" parameterType="map">
update t_user set name= #{name1},age=#{age} where id=#{id}
</update>
@Test
public void updateUserByMapTest(){
UserDao updateUserMapper = sqlSession.getMapper(UserDao.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("id",3);
map.put("age",30);
int rows = updateUserMapper.updateUserByMap(map);
if(rows>0){
System.out.println("您已成功更新"+rows+"条记录");
}else{
System.out.println("更新失败");
}
//务必记得提交事务
sqlSession.commit();
sqlSession.close();
}
六、模糊查询
七、配置解析
-
mybatis-config
-
相关属性
- configuration(配置)按顺序配置
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
7.1 environments(环境配置)
- configuration(配置)按顺序配置
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<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>
-
可以配置多种环境,但默认只能使用一个环境 (比如:default=“development”)。
-
每个 environment 元素定义的环境 ID(比如:id=“development”)。
-
事务管理器的配置(比如:type=“JDBC/MANAGED”)。
-
数据源的配置(比如:type=“UNPOOLED|POOLED|JNDI”)。
7.2 属性(properties)
外部进行配置
如db.properties
jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/mybatis? userSSL=true;useUnicode=true;characterEncoding=UTF-8
jdbc.username = root
jdbc.password = 123456
7.3 typeAliases(类型别名)
- 类型别名可为 Java 类型设置一个缩写名字
<typeAliases>
//为实体类起别名
<typeAlias type="com.kk.po.User" alias="User"/>
</typeAliases>
- 也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean 推荐使用
<typeAliases>
<package name="com.kk"/>
</typeAliases>
每一个在包 com.kk
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名,大写也可以。若有注解,则别名为其注解值。
@Alias("user1")
public class User {
...
}
7.4 设置(settings)
用于改变 MyBatis 的运行时行为
-
cacheEnabled 默认 true
-
lazyLoadingEnabled 默认false
7.5 映射器(mappers)
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。
- 方式一 使用相对于类路径的资源引用(任意情况都可用,推荐使用)
<mappers> <mapper resource="com/kk/dao/UserMapper.xml"/> </mappers>
- 方式二 使用映射器接口实现类的完全限定类名
<mappers> <mapper class="com.kk.dao.UserMapper"/> </mappers>
方式三 将包内的映射器接口实现全部注册为映射器
<mappers> <package name="com.kk.dao"/> </mappers>
注意点:
接口和它的接口实现Mapper文件必须同名,且在同一包下(方式二、方式三)
周期和作用域
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-coK2RYO7-1625567317569)(D:\桌面文件夹\生命周期.png)]
理解我们之前讨论过的不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder
- 一旦创建了 SqlSessionFactory,就不再需要它了
SqlSessionFactory
-
一旦被创建就应该在应用的运行期间一直存在, SqlSessionFactory 的最佳作用域是应用作用域
每个mapper,代表具体的一个业务
SqlSession
-
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
-
每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。
八、解决属性名和字段名不一致的问题
public class User {
private int id;
private String username;
private int age;
//此处省略getter、setter、、、
}
对应数据库
此时测试,我们会发现,由于数据库表中的name和java实体类的username不一致,导致查询的结果name为空
@Test
public void getUserById1(){
UserMapper getUserByIdMapper = sqlSession.getMapper(UserMapper.class);
User user = getUserByIdMapper.getUserById(2);
System.out.println(user);
sqlSession.close();
}
查询结果:User{id=2, username=‘null’, age=23}
2.解决方法:
-
在sql语句字段名起别名
<select id="getUserById" resultType="User" parameterType="Integer"> select id,name as username,age from t_user where id=#{id} </select>
-
resultMap(简单映射) 一对多
<mapper namespace="com.kk.dao.UserMapper"> <resultMap id="UserMap" type="User"> <!-- column数据库中的字段,property实体类中属性 一致的可以不进行映射--> <!-- <result column="id" property="id"/>--> <result column="name" property="username"/> <!-- <result column="age" property="age"/>--> </resultMap> <select id="getUserById" resultMap="UserMap" > select * from t_user where id=#{id} </select> </mapper>
九、日志
<settings>
<!--logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。默认STDOUT_LOGGING标准日志输出-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="logImpl" value="log4j"/>
</settings>
十、使用注解开发
1、使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
public interface BlogMapper {
@Select("SELECT * FROM blog WHERE id = #{id}")
Blog selectBlog(int id);
}
2、使用注解,还需在核心配置文件绑定接口
<mappers>
<mapper class="com.kk.dao.UserDao"/>
</mappers>
关于@Param()注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- SQL中引用的十@Param()中设定的属性名
十一、Lombok
-
安装插件
-
导入jar包
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
-
添加注解
在实体类添加@Data等注解
@Data public class User { private int id; private String name; private int age; }
十二、MyBatis关系映射
-
多对一 多个学生对应一个老师
按照结果嵌套
<select id="getStudents2" resultMap="StudentAndTeacher2"> select s.id sid,s.name sname,t.name tname,t.id tid from student s,teacher t where t.id = s.tid; </select> <resultMap id="StudentAndTeacher2" type="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="Teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> </association> </resultMap>
按照查询嵌套
<!--嵌套查询--> <select id="getStudents" resultMap="StudentAndTeacher"> select * from student; </select> <resultMap id="StudentAndTeacher" type="Student"> <!--针对单个属性,因为数据库字段和属性字段一一对应,可以省略不写--> <result property="id" column="id"/> <result property="name" column="name"/> <!--对于复杂的属性 1 对象 association 2 集合 collection --> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="teacher"> select * from teacher where id=#{id} </select>
测试
@Test public void test2(){ SqlSession sqlSession = MyBatisUtils.getSession(); TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class); Teacher teacher=teacherMapper.getTeacher1(1); System.out.println(teacher); sqlSession.close(); }
-
一对多 一个老师对应多个学生
按照结果嵌套
<select id="getTeacher1" resultMap="TeachersStudent1"> select t.id tid,t.name tname,s.name sname ,s.id sid from student s,teacher t where t.id = s.tid and t.id=#{tid}; </select> <resultMap id="TeachersStudent1" type="teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> <collection property="studentList" ofType="student" > <result property="id" column="sid"/> <result property="name" column="sname"/> <result property="tid" column="tid"/> </collection> </resultMap>
按照查询嵌套
<select id="getTeacher" resultMap="TeachersStudent"> select * from teacher where id = #{tid} </select> <resultMap id="TeachersStudent" type="Teacher"> //主键 <id property="id" column="id"/> <result property="name" column="name"/> <collection property="studentList" column="id" javaType="ArrayList" ofType="Student" select="getStudentById"/> </resultMap> <select id="getStudentById" resultType="Student" > select * from student where tid=#{id} </select>
测试
@Test public void test3(){ SqlSession sqlSession = MyBatisUtils.getSession(); TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class); Teacher teacher=teacherMapper.getTeacher(1); System.out.println(teacher); sqlSession.close(); }
-
多对多
-
javaType && ofType
- javaType用来指定实体类属性的类型
- ofTpye用来指定映射到List或集合中pojo类型,泛型中的约束类型
-
association(对象关联) && collection(集合)
- association 多对一
- collection 一对多
十三、动态SQL
搭建环境
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=UTF8