一、mybatis知识总结
pojo:不按mvc分层,只是java bean有一些属性,还有get、set方法
domain:不按mvc分层,只是java bean有一些属性,还有get、set方法
po:用在持久层,还可以再增加或者修改的时候,从页面直接传入action中,它里面的java bean 类名等于表名,属性名等于表的字段名,还有对应的get、set方法
vo: view object表现层对象,主要用于在高级查询中从页面接收传过来的各种参数.好处是扩展性强
bo: 用在servie层,现在企业基本不用.
这些po,vo, bo,pojo可以用在各种层面吗?
可以,也就是po用在表现层,vo用在持久层不报错,因为都是普通的java bean没有语法错误.但是在企业最好不要混着用,因为这些都是设计的原则,混着用比较乱.不利于代码维护.
自学方法论: 理论 -> 实践 -> 理论 -> 实践 反复迭代三遍
总结:
-
mybatis是一个持久层框架, 作用是跟数据库交互完成增删改查
-
原生Dao实现(需要接口和实现类)
-
动态代理方式(只需要接口)
mapper接口代理实现编写规则:
1)映射文件中namespace要等于接口的全路径名称
2)映射文件中sql语句id要等于接口的方法名称
3)映射文件中传入参数类型要等于接口方法的传入参数类型
4)映射文件中返回结果集类型要等于接口方法的返回值类型 -
#{}占位符:占位
如果传入的是基本类型,那么#{}中的变量名称可以随意写
如果传入的参数是pojo类型,那么#{}中的变量名称必须是pojo中的属性.属性.属性… - ${}拼接符:字符串原样拼接
如果传入的是基本类型,那么${}中的变量名必须是value
如果传入的参数是pojo类型,那么${}中的变量名称必须是pojo中的属性.属性.属性…
注意:使用拼接符有可能造成sql注入,在页面输入的时候可以加入校验,不可输入sql关键字,不可输入空格 - 映射文件:
1)传入参数类型通过parameterType属性指定
2)返回结果集类型通过resultType属性指定 -
hibernate和mybatis区别:
hibernate:它是一个标准的orm框架,比较重量级,学习成本高.
优点:高度封装,使用起来不用写sql,开发的时候,会减低开发周期.
缺点:sql语句无法优化
应用场景:oa(办公自动化系统), erp(企业的流程系统)等,还有一些政府项目等
总的来说,在用于量不大,并发量小的时候使用.
mybatis:它不是一个orm框架, 它是对jdbc的轻量级封装, 学习成本低,比较简单
优点:学习成本低, sql语句可以优化, 执行效率高,速度快
缺点:编码量较大,会拖慢开发周期
应用场景: 互联网项目,比如电商,P2P理财等
总的来说是用户量较大,并发高的项目.
========================================================================================== - 输入映射(就是映射文件中可以传入哪些参数类型)
1)基本类型
2)pojo类型
3)Vo类型 - 输出映射(返回的结果集可以有哪些类型)
1)基本类型
2)pojo类型
3)List类型 -
动态sql:动态的拼接sql语句,因为sql中where条件有可能多也有可能少
1)where:可以自动添加where关键字,还可以去掉第一个条件的and关键字
2)if:判断传入的参数是否为空
3)foreach:循环遍历传入的集合参数
4)sql:封装查询条件,以达到重用的目的 - 对单个对象的映射关系:
1)自动关联(偷懒的办法):可以自定义一个大而全的pojo类,然后自动映射其实是根据数据库
总的字段名称和pojo中的属性名称对应.
2)手动关联: 需要指定数据库中表的字段名称和java的pojo类中的属性名称的对应关系.
使用association标签 - 对集合对象的映射关系
只能使用手动映射:指定表中字段名称和pojo中属性名称的对应关系
使用collection标签 -
spring和mybatis整合
整合后会话工厂都归spring管理
1)原生Dao实现:
需要在spring配置文件中指定dao实现类
dao实现类需要继承SqlSessionDaoSupport超类
在dao实现类中不要手动关闭会话,不要自己提交事务.
2)Mapper接口代理实现:
在spring配置文件中可以使用包扫描的方式,一次性的将所有mapper加载 - 逆向工程:自动生成Pojo类,还可以自动生成Mapper接口和映射文件
注意:生成的方式是追加而不是覆盖,所以不可以重复生成,重复生成的文件有问题.
如果想重复生成将原来生成的文件删除
二、mybatis最初原始使用步骤总结
- 1)配置mybatis-config.xml 全局的配置文件 (1、数据源,2、外部的mapper)
- 2)创建SqlSessionFactory
- 3)通过SqlSessionFactory创建SqlSession对象
- 4)通过SqlSession操作数据库 CRUD
- 5)调用session.commit()提交事务
- 6)调用session.close()关闭会话
mybatis整体架构如下:
2.1、引入依赖(pom.xml)
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
2.2、全局配置文件(mybatis-config.xml)
<?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>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis-110?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
<!-- 环境,可以配置多个,default:指定采用哪个环境 -->
<environments default="test">
<!-- id:唯一标识 -->
<environment id="test">
<!-- 事务管理器,JDBC类型的事务管理器 -->
<transactionManager type="JDBC" />
<!-- 数据源,池类型的数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis-110" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
<environment id="development">
<!-- 事务管理器,JDBC类型的事务管理器 -->
<transactionManager type="JDBC" />
<!-- 数据源,池类型的数据源 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}" /> <!-- 配置了properties,所以可以直接引用 -->
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
</configuration>
2.3、配置Map.xml(MyMapper.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:根标签,namespace:命名空间,随便写,一般保证命名空间唯一 -->
<mapper namespace="MyMapper">
<!-- statement,内容:sql语句。id:唯一标识,随便写,在同一个命名空间下保持唯一
resultType:sql语句查询结果集的封装类型,tb_user即为数据库中的表
-->
<select id="selectUser" resultType="com.zpc.mybatis.User">
select * from tb_user where id = #{id}
</select>
</mapper>
2.4、修改全局配置文件(mybatis-config.xml)
引入MyMapper.xml
<configuration>
<!-- 环境,可以配置多个,default:指定采用哪个环境 -->
<environments default="test">
<!-- id:唯一标识 -->
<environment id="test">
<!-- 事务管理器,JDBC类型的事务管理器 -->
<transactionManager type="JDBC" />
<!-- 数据源,池类型的数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/ssmdemo" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!-- 引入MyMapper.xml -->
<mappers>
<mapper resource="mappers/MyMapper.xml" />
</mappers>
</configuration>
2.5、构建sqlSessionFactory(MybatisTest.java)
// 指定全局配置文件
String resource = "mybatis-config.xml";
// 读取配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
// 构建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
2.6、打开sqlSession会话,并执行sql(MybatisTest.java)
// 获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 操作CRUD,第一个参数:指定statement,规则:命名空间+“.”+statementId
// 第二个参数:指定传入sql的参数:这里是用户id
User user = sqlSession.selectOne("MyMapper.selectUser", 1);
System.out.println(user);
MybatisTest.java完整代码:
import com.zpc.test.pojo.User;
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 java.io.InputStream;
public class MybatisTest {
public static void main(String[] args) throws Exception {
// 指定全局配置文件
String resource = "mybatis-config.xml";
// 读取配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
// 构建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
// 操作CRUD,第一个参数:指定statement,规则:命名空间+“.”+statementId
// 第二个参数:指定传入sql的参数:这里是用户id
User user = sqlSession.selectOne("MyMapper.selectUser", 1);
System.out.println(user);
} finally {
sqlSession.close();
}
}
}
User.java
import java.text.SimpleDateFormat;
import java.util.Date;
public class User {
private String id;
private String userName;
private String password;
private String name;
private Integer age;
private Integer sex;
private Date birthday;
private String created;
private String updated;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getCreated() {
return created;
}
public void setCreated(String created) {
this.created = created;
}
public String getUpdated() {
return updated;
}
public void setUpdated(String updated) {
this.updated = updated;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", userName='" + userName + '\'' +
", password='" + password + '\'' +
", name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", birthday='" + new SimpleDateFormat("yyyy-MM-dd").format(birthday) + '\'' +
", created='" + created + '\'' +
", updated='" + updated + '\'' +
'}';
}
}
三、完整的CRUD操作(非动态代理方式,不推荐)
3.1、创建UserDao接口
import com.zpc.mybatis.pojo.User;
import java.util.List;
public interface UserDao {
/**
* 根据id查询用户信息
*
* @param id
* @return
*/
public User queryUserById(String id);
/**
* 查询所有用户信息
*
* @return
*/
public List<User> queryUserAll();
/**
* 新增用户
*
* @param user
*/
public void insertUser(User user);
/**
* 更新用户信息
*
* @param user
*/
public void updateUser(User user);
/**
* 根据id删除用户信息
*
* @param id
*/
public void deleteUser(String id);
}
3.2、创建UserDaoImpl
import com.zpc.mybatis.dao.UserDao;
import com.zpc.mybatis.pojo.User;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class UserDaoImpl implements UserDao {
public SqlSession sqlSession;
public UserDaoImpl(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public User queryUserById(String id) {
return this.sqlSession.selectOne("UserDao.queryUserById", id);
}
@Override
public List<User> queryUserAll() {
return this.sqlSession.selectList("UserDao.queryUserAll");
}
@Override
public void insertUser(User user) {
this.sqlSession.insert("UserDao.insertUser", user);
}
@Override
public void updateUser(User user) {
this.sqlSession.update("UserDao.updateUser", user);
}
@Override
public void deleteUser(String id) {
this.sqlSession.delete("UserDao.deleteUser", id);
}
}
3.3、编写UserDao对应的UserDaoMapper.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:根标签,namespace:命名空间,随便写,一般保证命名空间唯一 -->
<mapper namespace="UserDao">
<!-- statement,内容:sql语句。id:唯一标识,随便写,在同一个命名空间下保持唯一
resultType:sql语句查询结果集的封装类型,tb_user即为数据库中的表
-->
<!--<select id="queryUserById" resultType="com.zpc.mybatis.pojo.User">-->
<!--select * from tb_user where id = #{id}-->
<!--</select>-->
<!--使用别名-->
<select id="queryUserById" resultType="com.zpc.mybatis.pojo.User">
select
tuser.id as id,
tuser.user_name as userName,
tuser.password as password,
tuser.name as name,
tuser.age as age,
tuser.birthday as birthday,
tuser.sex as sex,
tuser.created as created,
tuser.updated as updated
from
tb_user tuser
where tuser.id = #{id};
</select>
<select id="queryUserAll" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user;
</select>
<!--插入数据-->
<insert id="insertUser" parameterType="com.zpc.mybatis.pojo.User">
INSERT INTO tb_user (
user_name,
password,
name,
age,
sex,
birthday,
created,
updated
)
VALUES
(
#{userName},
#{password},
#{name},
#{age},
#{sex},
#{birthday},
now(),
now()
);
</insert>
<update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
UPDATE tb_user
<trim prefix="set" suffixOverrides=",">
<if test="userName!=null">user_name = #{userName},</if>
<if test="password!=null">password = #{password},</if>
<if test="name!=null">name = #{name},</if>
<if test="age!=null">age = #{age},</if>
<if test="sex!=null">sex = #{sex},</if>
<if test="birthday!=null">birthday = #{birthday},</if>
updated = now(),
</trim>
WHERE
(id = #{id});
</update>
<delete id="deleteUser">
delete from tb_user where id=#{id}
</delete>
</mapper>
然后需要在mybatis-config.xml中添加配置:
<mappers>
<!-- 第一种方式:使用相对于类路径的资源引用 -->
<mapper resource="mappers/UserDaoMapper.xml"/>
<!-- 第二种方式:注解方式可以使用如下配置方式 -->
<mapper class="com.zpc.mybatis.dao.UserMapper"/>
</mappers>
四、动态代理Mapper实现类(推荐)
动态代理总结:
使用mapper接口不用写接口实现类即可完成数据库操作,使用非常简单,也是官方所推荐的使用方法。使用mapper接口的必须具备以下几个条件:
- Mapper的namespace必须和mapper接口的全路径一致
- Mapper接口的方法名必须和sql定义的id一致
- Mapper接口中方法的输入参数类型必须和sql定义的parameterType一致
- Mapper接口中方法的输出参数类型必须和sql定义的resultType/resultMap一致
4.1、创建UserMapper接口(对应原UserDao)
public interface UserMapper {
/**
* 登录(直接使用注解指定传入参数名称)
* @param userName
* @param password
* @return
*/
public User login(@Param("userName") String userName, @Param("password") String password);
/**
* 根据表名查询用户信息(直接使用注解指定传入参数名称)
* @param tableName
* @return
*/
public List<User> queryUserByTableName(@Param("tableName") String tableName);
/**
* 根据Id查询用户信息
* @param id
* @return
*/
public User queryUserById(Long id);
/**
* 查询所有用户信息
* @return
*/
public List<User> queryUserAll();
/**
* 新增用户信息
* @param user
*/
public void insertUser(User user);
/**
* 根据id更新用户信息
* @param user
*/
public void updateUser(User user);
/**
* 根据id删除用户信息
* @param id
*/
public void deleteUserById(Long id);
}
4.2、创建UserMapper.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:根标签,namespace:命名空间,随便写,一般保证命名空间唯一 ,为了使用接口动态代理,这里必须是接口的全路径名-->
<mapper namespace="com.zpc.mybatis.dao.UserMapper">
<!--
1.#{},预编译的方式preparedstatement,使用占位符替换,防止sql注入,一个参数的时候,任意参数名可以接收
2.${},普通的Statement,字符串直接拼接,不可以防止sql注入,一个参数的时候,必须使用${value}接收参数
-->
<select id="queryUserByTableName" resultType="com.zpc.mybatis.pojo.User">
select * from ${tableName}
</select>
<select id="login" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where user_name = #{userName} and password = #{password}
</select>
<!-- statement,内容:sql语句。
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
resultType:sql语句查询结果集的封装类型,使用动态代理之后和方法的返回类型一致;resultMap:二选一
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
-->
<select id="queryUserById" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where id = #{id}
</select>
<select id="queryUserAll" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user
</select>
<!-- 新增的Statement
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
useGeneratedKeys:开启主键回写
keyColumn:指定数据库的主键
keyProperty:主键对应的pojo属性名
-->
<insert id="insertUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id"
parameterType="com.zpc.mybatis.pojo.User">
INSERT INTO tb_user (
id,
user_name,
password,
name,
age,
sex,
birthday,
created,
updated
)
VALUES
(
null,
#{userName},
#{password},
#{name},
#{age},
#{sex},
#{birthday},
NOW(),
NOW()
);
</insert>
<!--
更新的statement
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
-->
<update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
UPDATE tb_user
<trim prefix="set" suffixOverrides=",">
<if test="userName!=null">user_name = #{userName},</if>
<if test="password!=null">password = #{password},</if>
<if test="name!=null">name = #{name},</if>
<if test="age!=null">age = #{age},</if>
<if test="sex!=null">sex = #{sex},</if>
<if test="birthday!=null">birthday = #{birthday},</if>
updated = now(),
</trim>
WHERE
(id = #{id});
</update>
<!--
删除的statement
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
-->
<delete id="deleteUserById" parameterType="java.lang.String">
delete from tb_user where id=#{id}
</delete>
</mapper>
4.3、全局配置文件mybatis-config.xml引入UserMapper.xml
<mappers>
<!-- 第一种方式:使用相对于类路径的资源引用 -->
<mapper resource="mappers/UserMapper.xml"/>
<!-- 第二种方式:注解方式可以使用如下配置方式 -->
<mapper class="com.zpc.mybatis.dao.UserMapper"/>
</mappers>
五、Mapper XML文件详解
5.1、#{}和${}区别
- #{}只是表示占位,与参数的名字无关,如果只有一个参数会自动对应,相当于PreparedStatement使用占位符去替换参数,可以防止sql注入。(相当于会加一对引号’xxxx’)
- ${}是进行字符串拼接,相当于sql语句中的Statement,使用字符串去拼接sql;$可以是sql中的任一部分传入到Statement中,不能防止sql注入。
1、=======================推荐=======================
/**
* 根据表名查询用户信息(直接使用注解指定传入参数名称)
*
* @param tableName
* @return
*/
public List<User> queryUserByTableName(@Param("tableName") String tableName);
<select id="queryUserByTableName" resultType="com.zpc.mybatis.pojo.User">
select * from ${tableName}
</select>
2、=======================#{}多个参数时=======================
/**
* 登录(直接使用注解指定传入参数名称)
*
* @param userName
* @param password
* @return
*/
public User login( String userName, String password);
<select id="login" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where user_name = #{userName} and password = #{password}
</select>
3、=======================报错=======================
org.apache.ibatis.exceptions.PersistenceException:
Error querying database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]
Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]
4、=======================解决方案一=======================
<select id="login" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where user_name = #{0} and password = #{1}
</select>
5、=======================解决方案二=======================
<select id="login" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where user_name = #{param1} and password = #{param2}
</select>
6、=======================最终解决方案=======================
/**
* 登录(直接使用注解指定传入参数名称)
*
* @param userName
* @param password
* @return
*/
public User login(@Param("userName") String userName, @Param("password") String password);
<select id="login" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where user_name = #{userName} and password = #{password}
</select>
通常在方法的参数列表上加上一个注释@Param(“xxxx”) 显式指定参数的名字,然后通过${}或#{}
sql语句动态生成的时候,使用${}
sql语句中某个参数进行占位的时候#{}
面试题(#、$区别):
/**
* #号
* @param username1
* @return
*/
User queryUserListByName1(@Param("username1") String username1);
/**
* $号
* @param username2
* @return
*/
User queryUserListByName2(@Param("username2") String username2);
<select id="queryUserListByName1" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user WHERE user_name=#{username1}
</select>
<select id="queryUserListByName2" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user WHERE user_name='${username2}'//手动加了引号
</select>
5.2、like模糊查询用法
mybatis中对于使用like来进行模糊查询的几种方式:
方式一:使用'%${xxxxx}%'
<select id="queryUsersLikeUserName" resultType="User">
select * from tb_user where user_name like '%${userName}%'
</select>
注意:由于$是参数直接注入的,导致这种写法大括号里面不能注明jdbcType(如:'%${userName,jdbcType=VARCHAR}%'),不然会报错
缺点:可能会引起sql的注入,平时尽量避免使用${...}
方式二:使用"%"#{userName}"%"
<select id="queryUsersLikeUserName" resultType="User">
select * from tb_user where user_name like "%"#{userName}"%"
</select>
注意:因为#{xxx}解析成sql语句时候,会在变量外侧自动加单引号'',所以这里 % 需要使用双引号"",不能使用单引号'',不然会查不到任何结果
方式三:使用CONCAT()函数连接参数形式
<select id="queryUsersLikeUserName" resultType="User">
select * from tb_user where user_name like CONCAT('%',#{userName},'%')
</select>
5.3、resultMap用法
resultMap是mybatis中最重要的元素,使用resultMap可以解决两大问题:
- POJO属性名和表结构字段名不一致的问题(有些情况下也不是标准的驼峰格式)
- 完成高级查询,比如说:一对一、一对多、多对多
解决表字段名和属性名不一致的问题有两种方法:
1、如果是驼峰似的命名规则可以在mybatis配置文件中设置
2、使用resultMap解决
<!--
type:返回的结果集对应的java的实体类型
id:resultMap的唯一标识
autoMapping:默认完成映射,如果已开启驼峰匹配,可以解决驼峰匹配
-->
<resultMap type="User" id="resultUser" autoMapping="true">
<!--
指定主键
cloumn:数据库中的列名
property:java实体类中的属性名
-->
<id column="id" property="id"/>
<!-- 使用result配置数据库列名和java实体类中的属性名对应 -->
<result column="user_name" property="userName"/>
</resultMap>
======使用======
<select id="queryUserById" resultMap="resultUser">
select * from tb_user where id = #{id}
</select>
5.4、sql抽取使用
格式:
<sql id="xxxx"></sql>
<include refId="xxxx" />
例如:在UserMapper.xml中定义如下片段:
<sql id="commonSql">
id,
user_name,
password,
name,
age,
sex,
birthday,
created,
updated
</sql>
则可以在UserMapper.xml中使用它:
<select id="queryUserById" resultMap="userResultMap">
select <include refid="commonSql"></include> from tb_user where id = #{id}
</select>
<select id="queryUsersLikeUserName" resultType="User">
select <include refid="commonSql"></include> from tb_user where user_name like "%"#{userName}"%"
</select>
六、动态sql
动态sql用法有:
- if标签
- choose、when、otherwise标签
- where、set标签
- foreach标签
- trim标签
6.1、if标签
字符串String类型:
<select id="queryUserList" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user WHERE sex=1
<if test="name!=null and name.trim()!=''">
and name like '%${name}%'
</if>
</select>
<select id="queryUserList" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user WHERE sex=1
<if test="name!=null and name!=''">
and name=#{name}
</if>
</select>
整型int类型:
<select id="queryUserList" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user WHERE sex=1
<if test="taskId!=null and taskId!=''">
and taskId=#{taskId}
</if>
</select>
注意:当传入的taskId为0时,此if条件语句是进不去的。因为Mybatis中if标签中比较传入数字taskId和字符串''时,会把这个字符串''转换成数字0,然后再和这个数字taskId判断,因此该if条件语句进不去
解决:如果存在taskId为0的情况,则改为如下所示:
<select id="queryUserList" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user WHERE sex=1
<if test="taskId!=null and taskId!='' or taskId==0">
and taskId=#{taskId}
</if>
</select>
6.2、choose、when、otherwise标签
<select id="queryUserListByNameOrAge" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where sex=1
<!--
1.一旦有条件成立的when,后续的when则不会执行
2.当所有的when都不执行时,才会执行otherwise
-->
<choose>
<when test="name!=null and name!=''">
and name like '%${name}%'
</when>
<when test="age!=null">
and age = #{age}
</when>
<otherwise>
and name='李知恩'
</otherwise>
</choose>
</select>
6.3、where和set标签
where标签使用:
<!--
<where>标签的作用:如果该便签包含的元素中有返回值,就插入一个where
当if条件不满足的时候,where元素中没有任何内容,所以SQL中不会出现where
-->
<select id="queryUserListByNameAndAge" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user
<!--如果多出一个and,会自动去除,如果缺少and或者多出多个and则会报错-->
<where>
<if test="name!=null and name.trim()!=''">
and name like '%${name}%'
</if>
<if test="age!=null">
and age = #{age}
</if>
</where>
</select>
set标签两种使用(一般用在update语句中):
方式一:推荐(加上trim标签)
<!--
1、某个属性若不符合if条件则那个属性不会被更新修改
2、<trim prefix="set" suffixOverrides=",">会去除多余的逗号
3、now()表示当前时间
-->
<update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
UPDATE tb_user
<trim prefix="set" suffixOverrides=",">
<if test="userName!=null">user_name = #{userName},</if>
<if test="password!=null">password = #{password},</if>
<if test="name!=null and name.length()>0">name = #{name},</if>
<if test="age!=null">age = #{age},</if>
<if test="sex!=null">sex = #{sex},</if>
date = now(),
<if test="birthday!=null">birthday = #{birthday},</if>
</trim>
where id = #{id}
</update>
方式二:
<!--
<set>标签的作用:如果该标签包含的元素中有返回值,就插入一个set
如果set后面的字符串是以逗号结尾的,就将最后这个逗号去除
-->
<update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
UPDATE tb_user
<set>
<if test="userName!=null">user_name = #{userName},</if>
<if test="password!=null">password = #{password},</if>
<if test="name!=null and name.length()>0">name = #{name},</if>
<if test="age!=null">age = #{age},</if>
<if test="sex!=null">sex = #{sex},</if>
date = now(),
<if test="birthday!=null">birthday = #{birthday},</if>
</set>
where id = #{id}
</update>
6.4、foreach标签
<select id="queryUserListByIds" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where id in
<foreach collection="idList" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
6.5、trim标签
<where>和<set>标签都可以用trim标签实现,并且底层就是通过TrimSqlNode实现的
<where>标签对应的trim实现:
<trim prefix="where" prefixOverride="AND | OR ">
<set>标签对应的trim实现:
<trim prefix="set" suffixOverrides=",">
提示:
prefixOverride中AND和OR后面的空格不能省略,为了避免匹配到andes或
orders等单词。实际上prefixOverride包含"AND""OR""AND\n""OR\n"
"AND\r""OR\r""AND\t""OR\t"
<trim>标签的四个属性:
prefix:当trim元素包含内容时,会给内容增加prefix指定的前缀
prefixOverride:当trim元素包含内容时,会把内容中匹配的前缀字符串去掉
suffix:当trim元素包含内容时,会给内容增加prefix指定的后缀
suffixOverride:当trim元素包含内容时,会把内容中匹配的后缀字符串去掉
七、高级查询
- 关联-association:用于一对一和多对一的关系
- 集合-collection:用于一对多的关系
例如:
public class Person{
private id;
private name;
private age;
// 身份证对象
private IDCard card; -->Person的resultMap中使用association
// 银行卡集合
private List<BankCard> cardList; -->Person的resultMap中使用collection
}
一对一的例子:
人和身份证的关系是一对一
public class Card {
private Integer id;
private String code;
// 省略get、set方法
}
public class User {
private Integer id;
private String name;
private Integer age;
private Card card;
//省略get、set方法
}
<mapper namespace="com.huawei.mapper.PersonMapper">
<resultMap type="com.huawei.pojo.Person" id="personMapper">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<association property="card" column="card_id"
select="com.huawei.mapper.CardMapper.selectCardById"
javaType="com.huawei.pojo.Card">
</association>
</resultMap>
<select id="selectPersonById" parameterType="int" resultMap="personMapper">
select * from tb_person where id = #{id}
</select>
</mapper>
<mapper namespace="com.huawei.mapper.CardMapper">
<select id="selectCardById" parameterType="int" resultType="com.huawei.pojo.Card">
select * from tb_card where id = #{id}
</select>
</mapper>
一对多的例子:
班级和学生是一对多的关系
public class Clazz {
private Integer id;
private String code;
private String name;
// 班级与学生是一对多的关系
private List<Student> studentList;
// 省略set/get方法
}
public class Student {
private Integer id;
private String name;
// 学生与班级是多对一的关系
private Clazz clazz;
// 省略set/get方法
}
collection来实现一对多的映射:
<mapper namespace="com.huawei.mapper.ClazzMapper">
<select id="selectClazzById" parameterType="int" resultMap="clazzResultMap">
select * from tb_clazz where id = #{id}
</select>
<resultMap type="com.huawei.pojo.Clazz" id="clazzResultMap">
<id property="id" column="id"/>
<result property="code" column="code"/>
<result property="name" column="name"/>
<!-- property: 指的是集合属性的值, ofType:指的是List集合中元素的类型, fetchType:设置lazy懒加载, javaType:集合类型 -->
<collection property="studentList" ofType="com.huawei.pojo.Student"
column="id" javaType="ArrayList"
fetchType="lazy" select="com.huawei.mapper.StudentMapper.selectStudentByClazzId">
<id property="id" column="id"/>
<result property="name" column="name"/>
</collection>
</resultMap>
</mapper>
多对一的例子:
association来实现多对一的映射:
<mapper namespace="com.huawei.mapper.StudentMapper">
<select id="selectStudentById" parameterType="int" resultMap="studentResultMap">
select * from tb_clazz c,tb_student s where c.id = s.id and s.id = #{id}
</select>
<select id="selectStudentByClazzId" parameterType="int" resultMap="studentResultMap">
select * from tb_student where clazz_id = #{id}
</select>
<resultMap type="com.huawei.pojo.Student" id="studentResultMap">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!-- property:定义对象的属性名 javaType:属性的类型 -->
<association property="clazz" javaType="com.huawei.pojo.Clazz">
<id property="id" column="id"/>
<result property="code" column="code"/>
<result property="name" column="name"/>
</association>
</resultMap>
</mapper>
八、sql语句中特殊字符’<’处理
在xml中,”<”、”>”、”&”等字符是不能直接存入的,否则xml语法检查时会报错,如果想在xml中使用这些符号,必须将其转义。
方法一:使用xml中的字符实体转义替代
& & 和号
< < 小于
> > 大于
" " 双引号
' ' 单引号
如:<if test="startDateTime!=null"> and mm.ttime > to_date(#{startDateTime},'yyyy-mm-dd hh24:mi:ss')</if>
注意:
(1) 转义序列字符之间不能有空格
(2) 转义序列必须以";"结束
(3) 单独出现的"&"不会被认为是转义的开始
(4) 区分大小写
方法二:使用<![CDATA[]]>这个标记将包含的内容将表示为纯文本
例如:
<select id="query" parameterType="Entity" resultType="java.util.HashMap">
SELECT id, name, age
FROM person
WHERE age <![CDATA[ > ]]> 23 and id <![CDATA[ <= ]]> 100
</select>
注意:
(1) 不允许嵌套使用
(2) "]]>"这部分不能包含空格或者换行
九、缓存机制
Mybatis缓存机制分为一级缓存和二级缓存
在mybatis中,一级缓存默认是开启的,并且一直无法关闭。一级缓存是sqlSession级的,即利用同一个sqlSession执行查询相同的SQL,数据会直接从缓存中取。一个sqlSession会对应new一个调度器Excutor对象,每次Excutor去执行SQL前都会去缓存里看一下是不是之前执行过了,当然了本质上就是从一个Map里面找key的操作,执行过了就直接去缓存里取,没有则访问数据库。
二级缓存是Mapper级别的,可以这么理解只要是执行了同一个mapper.xml中的同一个SQL,都会直接从缓存里取,不管是不是同一个sqlSession。二级缓存需要去自己开启,在mapper.xml中写
一级缓存注意:
- MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据
- MyBatis一级缓存的生命周期和SqlSession一致
- MyBatis一级缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺
- mybatis和spring整合后进行mapper代理开发,不支持一级缓存
二级缓存注意:
- MyBatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强
- MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻
- 在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis,Memcached等分布式缓存可能成本更低,安全性也更高
二级缓存的脏读:
1、脏读的产生
Mybatis的二级缓存是和命名空间绑定的,所以通常情况下每一个Mapper映射文件都有自己的二级缓存,不同的mapper的二级缓存互不影响。这样的设计一不注意就会引起脏读,从而导致数据一致性的问题。引起脏读的操作通常发生在多表关联操作中,比如在两个不同的mapper中都涉及到同一个表的增删改查操作,当其中一个mapper对这张表进行查询操作,此时另一个mapper进行了更新操作刷新缓存,然后第一个mapper又查询了一次,那么这次查询出的数据是脏数据。出现脏读的原因是他们的操作的缓存并不是同一个。
2、脏读的避免
- mapper中的操作以单表操作为主,避免在关联操作中使用mapper
- 在关联操作的mapper中配置参照缓存
3、避免脏读的配置
Mapper接口和XML配置使用的是同一个命名空间。因此他们只能使用同一个缓存。
法一:
在Mapper接口中配置缓存然后在XML使用参照缓存
@CacheNarnespaceRef(RoleMapper.class) public interface RoleMapper { }
<!--那么在XML中只能使用参照缓存-->
<cache-ref narnespace="com.huawei.mapper.RoleMapper"/>
法二:
或者可以配置XML然后Mapper接口使用参照缓存
@CacheNarnespaceRef(RoleMapper.class) public interface RoleMapper { }
<mapper narnespace="com.huawei.mapper.RoleMapper">
<cache eviction="FIFO" flushinterval="60000" Size="512" readOnly="false"/>
</mapper>
总结:
[1]https://www.cnblogs.com/diffx/p/10611082.html
[2]https://blog.csdn.net/zhenwei1994/article/details/81876278
[3]https://blog.csdn.net/wang852575989/article/details/100173875
[4]https://blog.csdn.net/yangbaggio/article/details/89711259
[5]https://www.cnblogs.com/songsongblue/p/10185569.html
[6]https://blog.csdn.net/weixin_42447959/article/details/90746328
[7]https://tech.meituan.com/2018/01/19/mybatis-cache.html
[8]https://www.cnblogs.com/lanqi/p/9283516.html