MyBatis知识点总结
MyBatis 3.5.5
https://mybatis.org/mybatis-3/zh/index.html
一、MyBatis基本环境搭建
1、pom.xml
<dependencies> <!--日志打印--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.11.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.18</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> </build>
2、实体
@Getter @Setter @ToString @NoArgsConstructor @AllArgsConstructor public class Employee implements Serializable { private static final long serialVersionUID = -24165170760748761L; private Integer id; private String empName; private String empGender; private String email; private Date birthday; private Date updatetime; private Date createtime; }
3、Dao
public interface EmployeeDao { Employee getEmployeeById(Integer id); int addEmployee(Employee employee); int deleteEmployeeById(Integer id); int updateEmployee(Employee employee); }
4、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="org.lc.dao.EmployeeDao"> <!--增删改无需写返回类型。 自动接收返回影响的行数即可--> <!--如果是boolean 影响0行自动封装为false,否则为true--> <insert id="addEmployee"> insert into employee(emp_name,emp_gender,email,birthday,updatetime,createtime) values(#{empName},#{empGender},#{email},#{birthday},#{updatetime},#{createtime}) </insert> <update id="updateEmployee"> update employee set emp_name=#{empName},emp_gender=#{empGender},email=#{email},birthday=#{birthday} </update> <delete id="deleteEmployeeById"> delete from employee where id=#{id} </delete> <!--查询需要写返回类型--> <select id="getEmployeeById" resultType="Employee"> select * from employee where id=#{id}; </select> </mapper>
5、jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=123456
jdbc.url=jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
6、log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="INFO">
<appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="%d{yyyy.MM.dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
<SizeBasedTriggeringPolicy size="5 MB"/>
</RollingFile>
</appenders>
<loggers>
<root level="DEBUG">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFile"/>
</root>
</loggers>
</configuration>
核心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>
<!--和spring中的一样:<context:property-placeholder location="classpath:jdbc.properties" />-->
<!--引入外部的jdbc配置文件
resource:从类路径获取
url:从磁盘或者网络资源路径
-->
<properties resource="jdbc.properties"></properties>
<!--这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。-->
<settings>
<!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
<!--数据库中的字段一般不需要大写,到大写的字母之前加下划线即可,开启后 视图bean直接以驼峰命名即可-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--为类型起别名-->
<typeAliases>
<!--默认别名为 '类名'
alias:起别名
-->
<!--一次只能指定一个类型的别名-->
<!--<typeAlias type="org.lc.entity.Employee" alias="e"/>-->
<!--推荐全类名,不用别名-->
<!--为该包下的所有都起别名 默认为类名-->
<package name="org.lc.entity"/>
</typeAliases>
<!--自定义类型处理器-->
<!--<typeHandlers>-->
<!-- <typeHandler handler=""-->
<!--</typeHandlers>-->
<!--配置运行的environments环境-->
<!--配置 事务管理器和数据库连接池(默认使用JDBC的数据源和事务管理器)-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!--配置连接池-->
<dataSource type="POOLED">
<!--通过 ${}取出-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--mybatis用来考数据库移植的-->
<!--type: 固定写法-->
<databaseIdProvider type="DB_VENDOR">
<!--name:数据库厂商标识 value:别名-->
<property name="MySQL" value="mysql"/>
<property name="SQL Server" value="sqlserver"/>
<property name="Oracle" value="oracle"/>
<!-- mapper中的使用 通过databaseId指定即可-->
<!-- <select id="getEmployeeById" resultType="Employee" databaseId="oracle">
select * from employee where id=#{id};
</select>-->
</databaseIdProvider>
<!--映射接口的实现文件-->
<mappers>
<!--
resource: 从类路径映射的xml
url:引用磁盘或网络路径
class: 接口的全类名
-->
<!--一次只能映射一个mapper,xml文件必须放在在resources文件夹下-->
<!--<mapper resource="mapper/EmployeeDao.xml"></mapper>-->
<!--一次只能映射一个接口,若映射指定接口,则需要将xml放在和接口同包下,且名称必须相同。并且需要在maven配置中过滤掉xml文件-->
<!--<mapper class="org.lc.dao.EmployeeDao"></mapper>-->
<!--映射该包下的所有接口,需要将xml文件和接口放在一起,但是我们还是想将xml放在resources下,则需要在
resources下建一个和接口同样的包名,等编译打包之后会合并在一起
注意:建包的时候需要一级一级的键。一起键会当做一个包
-->
<package name="org.lc.dao"/>
</mappers>
</configuration>
测试
public class T1 {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
static SqlSessionFactory sqlSessionFactory=null;
static {
//1、根据全局配置文件创建一个SqlsessionFactory
//SqlSessionFactory是SqlSession的工厂,用于创建SqlSession对象
//SqlSession:sql会话(代表和数据库的一次会话)
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void Test() throws IOException {
//2、获取数据库的一次会话 相当于getConnection
//默认jdbc为手动提交,需要传入true自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
try {
//3、使用SqlSession操作数据库,获取到dao的实现接口
EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);
//调用接口的方法
System.out.println(mapper.getEmployeeById(1));
} finally {
//或者在后面设置自动提交
// sqlSession.commit();
sqlSession.close();
}
}
@Test
public void Test1() throws ParseException {
//2、获取数据库的一次会话 相当于getConnection
SqlSession sqlSession = sqlSessionFactory.openSession(true);
try {
//3、使用SqlSession操作数据库,获取到dao的实现接口
EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);
mapper.addEmployee(new Employee(null, "张三4","男","42119@qq.com",simpleDateFormat.parse("1999-09-09"),new Date(),new Date()));
} finally {
// sqlSession.commit();
sqlSession.close();
}
}
}
二、SQL映射文件
1、插入后获取自增主键的值
<!--
useGeneratedKeys: 是否使用自增的主键
keyProperty:将获取的主键值 赋值给那个属性
-->
<insert id="addEmployee" useGeneratedKeys="true" keyProperty="id">
insert into employee(emp_name,emp_gender,email,birthday,updatetime,createtime) values(#{empName},#{empGender},#{email},#{birthday},#{updatetime},#{createtime})
</insert>
@Test
public void Test1() throws ParseException {
SqlSession sqlSession = sqlSessionFactory.openSession(true);
try {
EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);
Employee employee = new Employee(null, "张三5", "男", "42119@qq.com", simpleDateFormat.parse("1999-09-09"), new Date(), new Date());
mapper.addEmployee(employee);
//封装到id属性中
System.out.println("自增的id:"+employee.getId());
} finally {
sqlSession.close();
}
}
2、插入前获取自增主键的值
<insert id="addEmployee2">
-- resultType:返回查询的类型
-- keyProperty: 赋值给指定bean的属性
-- order:在sql执行前运行。查询出来的值会赋值给bean中id属性后再运行sql
<selectKey resultType="integer" keyProperty="id" order="BEFORE">
select max(id)+1 from employee
</selectKey>
insert into employee(id,emp_name,emp_gender,email,birthday,updatetime,createtime) values(#{id},#{empName},#{empGender},#{email},#{birthday},#{updatetime},#{createtime})
</insert>
@Test
public void Test1() throws ParseException {
//2、获取数据库的一次会话 相当于getConnection
SqlSession sqlSession = sqlSessionFactory.openSession(true);
try {
//3、使用SqlSession操作数据库,获取到dao的实现接口
EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);
Employee employee = new Employee(null, "张三6", "男", "42119@qq.com", simpleDateFormat.parse("1999-09-09"), new Date(), new Date());
mapper.addEmployee2(employee);
System.out.println("获取执行sql前的id:"+employee.getId());
} finally {
// sqlSession.commit();
sqlSession.close();
}
}
3、传入各种参数和取值@param
1)单个参数
①基本类型 Employee getEmployeeById(Integer id);
- 取值:
#{id}
②引用类型
JavaBean 对象
int addEmployee(Employee employee);
- 取值:
#{Employee的属性名}
例:#{empName}
- 取值:
数组对象
int deleteMultipleEmps(@Param("id") Integer[] id);
必须加上**@Param("id")**,并指定参数名称<delete id="deleteMultipleEmps"> delete from employee where id in <foreach collection="id" item="i" separator="," open="(" close=")"> #{i} </foreach> </delete>
或者直接通过索引拿
delete from employee where id=#{id[0]} and #{id[1]}
List集合
int addEmployeeList(@Param("list") List<Employee> list);
必须加上**@Param("list")**,并指定参数名称<insert id="addEmployeeList"> insert into employee(name, gender, birthday, idCard) values <foreach collection="list" item="emp" separator=","> (#{emp.name}, #{emp.gender},#{emp.birthday},#{emp.idCard}) </foreach> </insert>
Map集合
Employee getEmployeeById2(Map<String,Object> map);
实际上我们所有的参数都封装为map集合 直接通过map的key取值 例如:
#{key值}
<select id="getEmployeeById2" resultType="org.lc.entity.Employee"> select * from employee where id=#{id} and emp_name=#{empName} </select>
EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class); //调用接口的方法 HashMap<String,Object> map=new HashMap<>(); map.put("id", "1"); map.put("empName", "张三"); System.out.println(mapper.getEmployeeById2(map));
2)多个参数
①基本类型 int updatePassword(@Param("encodepass") String encodepass,@Param("hrid") Integer hrid);
必须都加上**@param**并为其指定别名
- 取值:
#{encodepass}
,#{hrid}
②不同类型 Long getTotal(@Param("employee") Employee employee,@Param("beginDateScope") Date[] beginDateScope);
同时指定**@param**注解并为其取别名
- javaBean 取值
#{别名.属性名}
例如:#{employee.empName}
- 数组取值:
#{beginDateScope[0]}
或者 foreach遍历取值 - 基本类型 :
#{别名}
③Map集合+其他类型 Employee getEmployeeById2(@Param("map") Map<String,Object> map,@Param("gender") String gender);
必须都加上**@param**并为其指定别名
直接通过 map . 键值获取 例如: #{map.键值}
<select id="getEmployeeById2" resultType="org.lc.entity.Employee">
select * from employee where id=#{map.id} and emp_name=#{map.empName} and emp_gender=#{gender}
</select>
EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);
//调用接口的方法
HashMap<String,Object> map=new HashMap<>();
map.put("id", "1");
map.put("empName", "张三");
System.out.println(mapper.getEmployeeById2(map,"男"));
4、取值时jdbcType的作用
id=#{id,jdbcType=INT}
- 默认不指定jdbcType,mysql没问题,oracle没问题
- 如果传入的为null, 则mysql插入null没问题,oracle不指定null到底是什么类型
5、#{} 与 ${} 的区别
#{} : 是参数的预编译方式,参数的位置都是用 ?代替,参数后来都是预编译设置进去的,不会有sql注入问题
${} :不是参数预编译的方式,而是将 参数 直接和sql拼接,存在sql注入问题,不安全。
${}
可以在不支持参数预编译的位置进行取值就使用 ${}。例如:动态注入表名
6、查询返回List集合
List<Employee> getAllEmployee();
<!--直接指定返回的集合中的类型即可-->
<select id="getAllEmployee" resultType="org.lc.entity.Employee">
select * from employee
</select>
7、增删改无需写返回类型,只需接收执行影响的行数
8、resultType默认返回类型对应的别名
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
9、查询返回Map集合
1)返回单条集合
Map<String, Object> getEmployeeByIdReturnMap(int id);
<select id="getEmployeeByIdReturnMap" resultType="map">
select * from employee where id=#{id}
</select>
返回的map集合为 列名作为key, 值作为value
2)返回多条集合@MapKey
//MapKey指定value中JavaBean对象的某个属性作为map中的key (一般我们指定返回值中的主键作为key)
@MapKey("id")
Map<Integer, Employee> getAllEmployeeReturnMap();
<!--resultType返回的类型为 map中的value类型-->
<select id="getAllEmployeeReturnMap" resultType="org.lc.entity.Employee">
select * from employee
</select>
EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);
Map<Integer, Employee> employeeByIdReturnMap = mapper.getAllEmployeeReturnMap();
System.out.println(employeeByIdReturnMap.get(1).toString());
System.out.println(employeeByIdReturnMap);
10、配置驼峰命名法mapUnderscoreToCamelCase
实体属性
isParent
或者isparent
都可以进行对数据库字段isparent
进行映射(mysql关闭区分大小写)开启驼峰命名后:实体属性
isParent
可以对数据库字段is_parent
进行自动映射 。或者实体属性vhrIsParent
可以对数据库字段vhr_is_parent
进行自动映射。若映射失败,则该实体属性字段查询为空。windows下数据库的表名和字段名不区分大小写,但是在Linux区分大小写,我们建议在 数据库表名统一小写,字段名若要大写则前面加下划线 。
- 正确实例:emp_name 错误实例:empName
Mybatis开启驼峰命名法后
- 生成代码:若采用生成代码则自动 将 数据库的字段名 emp_name 生成对应的 JavaBean属性名 empName
- 返回结果集:若直接resultType指定JavaBean,则会把 emp_name 映射成 JavaBean属性名 empName,找不到则报错
11、resultMap自定义封装结果集
1)字段属性映射
若驼峰命名法也不能解决返回的结果集,则需要自定义结果集
数据库字段:
bean:
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Hr implements Serializable {
private static final long serialVersionUID = 177623542882739248L;
/**
* hrID
*/
private Integer id;
/**
* 姓名
*/
private String name;
/**
* 手机号码
*/
private String phone;
/**
* 住宅电话
*/
private String telephone;
}
dao
public interface HrDao {
Hr getHrById(int id);
}
mapper
<?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="org.lc.dao.HrDao">
<!--自定义结果集-->
<!--id:结果集唯一id-->
<!--type:结果集的javabean类型-->
<resultMap id="BaseResultMap" type="org.lc.entity.Hr">
<!--使用id标签标识主键-->
<!--property: javabean中的属性名 -->
<!--column: 数据库对应的字段名 -->
<id property="id" column="hrid"></id>
<result property="name" column="hrname"/>
<result property="phone" column="hrphone"/>
<result property="telephone" column="hrtelephone"/>
</resultMap>
<!--resultMap: 指定自定义结果集的id-->
<select id="getHrById" resultMap="BaseResultMap">
select * from hr where hrid=#{id}
</select>
</mapper>
1)resultMap一对一映射
数据库字段
bean
@Getter @Setter @ToString @NoArgsConstructor @AllArgsConstructor public class Hr implements Serializable { private static final long serialVersionUID = 177623542882739248L; /** * hrID */ private Integer id; /** * 姓名 */ private String name; /** * 手机号码 */ private String phone; /** * 住宅电话 */ private String telephone; //一对一关联对象 private Department department; }
@Getter @Setter @ToString @NoArgsConstructor @AllArgsConstructor public class Department implements Serializable { private static final long serialVersionUID = -43971297416251267L; private Integer id; /** * 部门名称 */ private String name; private int enabled; }
dao
public interface HrDao { Hr getHrAndDepartment(int hrid); }
mapper
<?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="org.lc.dao.HrDao"> <resultMap id="HrAndDepartment" type="org.lc.entity.Hr"> <id property="id" column="hrid"></id> <result property="name" column="hrname"/> <result property="phone" column="hrphone"/> <result property="telephone" column="hrtelephone"/> <!--关联的对象--> <!--javaType:对象类型--> <association property="department" javaType="org.lc.entity.Department"> <id property="id" column="depid"></id> <result property="name" column="depName"></result> <result property="enabled" column="enabled"></result> </association> </resultMap> <select id="getHrAndDepartment" resultMap="HrAndDepartment"> SELECT hrId, hrName, hrPhone, hrTelephone, d.depId AS depid, depName, enabled FROM hr, department AS d WHERE hr.depid = d.depId and hrid=#{hrid} </select> </mapper>
测试
@Test public void Test1(){ SqlSession sqlSession = sqlSessionFactory.openSession(true); HrDao mapper = sqlSession.getMapper(HrDao.class); Hr hrById = mapper.getHrAndDepartment(5); System.out.println(hrById); sqlSession.close(); }
2)resultMap一对多映射
数据库
bean
@Getter @Setter @ToString @NoArgsConstructor @AllArgsConstructor public class Hr implements Serializable { private static final long serialVersionUID = 177623542882739248L; /** * hrID */ private Integer id; /** * 姓名 */ private String name; /** * 手机号码 */ private String phone; /** * 住宅电话 */ private String telephone; //一对一关联对象 private Department department; //一对多关联 private List<Role> roles; }
@Getter @Setter @ToString @NoArgsConstructor @AllArgsConstructor public class Role implements Serializable { private static final long serialVersionUID = 457993748452345260L; private Integer id; private String name; /** * 角色名称 */ private String namezh; }
dao
public interface HrDao { Hr getHrAndDepartmentAndRole(int hrid); }
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="org.lc.dao.HrDao"> <resultMap id="HrAndDepartmentAndRole" type="org.lc.entity.Hr"> <id property="id" column="hrid"></id> <result property="name" column="hrName"></result> <result property="phone" column="hrPhone"></result> <result property="telephone" column="hrTelephone"></result> <!--一对一对象关联--> <!--javaType:指定对象类型--> <association property="department" javaType="org.lc.entity.Department"> <id property="id" column="depid"></id> <result property="name" column="depName"></result> <result property="enabled" column="enabled"></result> </association> <!--一对多关联--> <!--ofType:指定集合中的类型--> <collection property="roles" ofType="org.lc.entity.Role"> <id property="id" column="rid"></id> <result property="name" column="rname"></result> <result property="namezh" column="rnamezh"></result> </collection> </resultMap> <select id="getHrAndDepartmentAndRole" resultMap="HrAndDepartmentAndRole"> select hr.hrid as hrid, hrName, hrPhone, hrTelephone, d.depId AS depid, depName, enabled, r.id AS rid, r.`name` AS rname, r.nameZh AS rnamezh FROM hr LEFT JOIN department AS d ON hr.depId = d.depId LEFT JOIN hr_role ON hr.hrId = hr_role.hrid LEFT JOIN role AS r ON hr_role.rid = r.id where hr.hrid=#{hrid} </select> </mapper>
测试
@Test public void Test2(){ SqlSession sqlSession = sqlSessionFactory.openSession(true); HrDao mapper = sqlSession.getMapper(HrDao.class); Hr hrById = mapper.getHrAndDepartmentAndRole(3); System.out.println(hrById); sqlSession.close(); }
12、分步查询、懒加载(不常用)
<resultMap id="HrAndDepartmentAndRole1" type="org.lc.entity.Hr">
<id property="id" column="hrid"></id>
<result property="name" column="hrName"></result>
<result property="phone" column="hrPhone"></result>
<result property="telephone" column="hrTelephone"></result>
<result property="department.id" column="depid"/>
<!--一对一对象关联-->
<!--分布查询 并设置 懒加载(即用到该属性时使用此sql查询)-->
<!---->
<association property="department" column="depid" select="通过其他select标签的全类名加方法名 将该column的值作为参数传递给该select查询。并将查询出来的值关联department" fetchType="lazy">
</association>
<!--一对多关联-->
<!--分布查询 并设置 懒加载(即用到该属性时使用此sql查询)-->
<collection property="roles" select="" column="">
</collection>
</resultMap>
在标签内设置外,还可以在mybatis-cofig.xml
中配置
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
三、动态SQL
1、xml特殊字符转义
< | 小于 | < | < |
---|---|---|---|
> | 大于 | > | > |
& | &符号 | & | & |
" | 双引号 | " | " |
© | 版权 | © | © |
® | 已注册商标 | ® | ® |
™ | 商标(美国) | ™ | ™ |
× | 乘号 | × | × |
÷ | 除号 | ÷ | ÷ |
2、dao bean参考
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Employee implements Serializable {
private static final long serialVersionUID = -24165170760748761L;
private Integer id;
private String empName;
private String empGender;
private String email;
private Date birthday;
private Date updatetime;
private Date createtime;
}
public interface EmployeeDao {
//有条件的查询
List<Employee> getEmployeeBySelective(Employee employee);
//有添加的新增
int addEmployeeSelective(Employee employee);
//有条件的更新
int updateEmployeeSelective(Employee employee);
//有条件的删除
int deleteEmployeeSelective(Employee employee);
int deleteEmployeeMultiple(@Param("ids") Integer[] ids);
int addEmployeeList(@Param("list") List<Employee> list);
}
3、where if标签的使用
动态条件查询
下面这种写法,
userId
和matchId
必传,where
条件必然成立。这里的第一个if
标签中的and
关键字必须
<select id="findAccount" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM t_match_account
<where>
USER_ID = #{userId}
and MATCH_ID = #{matchId}
and STATUS = '0'
<if test="accountType != null and accountType != ''">
and ACCOUNT_TYPE = #{accountType}
</if>
</where>
</select>
List<Employee> getEmployeeBySelective(Employee employee);
<select id="getEmployeeBySelective" resultType="org.lc.entity.Employee">
select * from employee
<!--若where中的标签有一个条件满足则 自动加where关键字。若没有条件成立则自动去掉where关键字-->
<!--where会为第一个判断为true的if标签(包括第一个if前面如果有and也会去掉)去掉前面的and关键字-->
<where>
<!-- 判断id不为null-->
<if test="id!=null">
id=#{id}
</if>
<!--判断empName不为null 并且 empName不为空字符串-->
<if test="empName!=null and empName!=''">
and emp_name=#{empName}
</if>
<!--判断empGender不为null 并且 empGender不为空字符串-->
<if test="empGender!=null and !empGender.equals('')">
and emp_gender=#{empGender}
</if>
<if test="email!=null and email!=''">
and email=#{email}
</if>
<if test="birthday!=null">
and birthday > #{birthday}
</if>
<!--判断createtime不为null-->
<!--直接写 < 可能在xml识别为xml语法,需要将小于号转义为 <-->
<if test="createtime!=null">
and createtime < #{createtime}
</if>
<if test="updatetime!=null">
and updatetime = #{updatetime}
</if>
</where>
</select>
动态条件删除(慎用)
int deleteEmployeeSelective(Employee employee);
若所有条件不成立则会删除表
<delete id="deleteEmployeeSelective">
delete from employee
<where>
<if test="id!=null">
id=#{id}
</if>
<if test="empName!=null and empName!=''">
and emp_name=#{empName}
</if>
<if test="empGender!=null and !empGender.equals('')">
and emp_gender=#{empGender}
</if>
<if test="email!=null and email!=''">
and email=#{email}
</if>
<if test="birthday!=null">
and birthday > #{birthday}
</if>
<if test="createtime!=null">
and createtime < #{createtime}
</if>
<if test="updatetime!=null">
and updatetime = #{updatetime}
</if>
</where>
</delete>
4、trim if标签的使用
int addEmployeeSelective(Employee employee);
动态条件插入
<insert id="addEmployeeSelective">
insert into employee
<!--去重-->
<!--若有某一个条件成立,则加上前缀和后缀,否则trim所有条件失效-->
<!--prefix 为下面整块sql添加前缀-->
<!--suffix 为下面整块sql添加后缀-->
<!--prefixOverrides 为某一个成立的条件去掉的前缀字符-->
<!--suffixOverrides 为某一个成立的条件去掉的后缀字符-->
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="id!=null">
id,
</if>
<if test="empName!=null and empName!=''">
emp_name,
</if>
<if test="empGender!=null and empGender!=''">
emp_gender,
</if>
<if test="email!=null and email!=''">
email,
</if>
<if test="birthday!=null">
birthday,
</if>
<if test="updatetime!=null">
updatetime,
</if>
<if test="createtime!=null">
createtime,
</if>
</trim>
<trim prefix="values(" suffix=")" suffixOverrides="," >
<if test="id!=null">
#{id},
</if>
<if test="empName!=null and empName!=''">
#{empName},
</if>
<if test="empGender!=null and empGender!=''">
#{empGender},
</if>
<if test="email!=null and email!=''">
#{email},
</if>
<if test="birthday!=null">
#{birthday},
</if>
<if test="updatetime!=null">
#{updatetime},
</if>
<if test="createtime!=null">
#{createtime},
</if>
</trim>
</insert>
5、set if标签的使用
动态条件更新
int updateEmployeeSelective(Employee employee);
<update id="updateEmployeeSelective">
update employee
<!--set中的的条件有一个成立,则自动加上set关键字,若所有条件不成立 则会报错: update employee where id=? 不成立-->
<!--set会为最后一个成立的if去掉逗号 ,-->
<set>
<if test="empName!=null and empName!=''">
emp_name=#{empName},
</if>
<if test="empGender!=null and empGender!=''">
emp_gender=#{empGender},
</if>
<if test="email!=null and email!=''">
email=#{email},
</if>
<if test="birthday!=null">
birthday=#{birthday},
</if>
<if test="updatetime!=null">
updatetime=#{updatetime},
</if>
<if test="createtime!=null">
createtime=#{createtime},
</if>
</set>
where id=#{id}
</update>
6、foreach的使用
查询/删除多条数据(遍历数组)
这里的参数必须取别名
int deleteEmployeeMultiple(@Param("ids") Integer[] ids);
<delete id="deleteEmployeeMultiple">
delete from employee where id in
<!--若数组为空则条件不成立会报错:delete from employee where id in-->
<!--foreach遍历数组-->
<!--collection:一般为被@param命名的集合别名-->
<!--item:当前遍历的元素-->
<!--open:当前sql开始的前缀-->
<!--close:当前sql结束的后缀-->
<!--separator:每个元素之间的分隔符-->
<!--index:
若为数组/List集合:则为当前索引
若为map结合:则代表当前key
-->
<foreach collection="ids" item="id" open="(" close=")" separator="," >
#{id}
</foreach>
</delete>
批量插入
这里的参数必须取别名
int addEmployeeList(@Param("list") List<Employee> list);
<insert id="addEmployeeList">
insert into employee(emp_name,emp_gender,email,birthday,updatetime,createtime) values
<!--注意批量插入的格式为:insert into employee(emp_name,emp_gender,email,birthday,updatetime,createtime) values (?,?,?,?,?,?) , (?,?,?,?,?,?) , (?,?,?,?,?,?)-->
<!--所以使用在foreach自动在没一次遍历加 ( 和 ) 需要手动加括号作为一次分段整体的添加-->
<foreach collection="list" item="emp" separator=",">
(#{emp.empName},#{emp.empGender},#{emp.email},#{emp.birthday},#{emp.updatetime},#{emp.createtime})
</foreach>
</insert>
7、chose when otherwise的使用
if-else查询
List<Employee> getEmployeeByWhen(Employee employee);
<select id="getEmployeeByWhen" resultType="org.lc.entity.Employee">
select * from employee
<where>
<choose>
<!--从上到下,只要满足一个when的条件,则后序条件都不执行-->
<when test="id!=null">
id=#{id}
</when>
<when test="empName!=null and empName!=''">
emp_name=#{empName}
</when>
<when test="empGender!=null and empGender!=''">
emp_gender=#{empGender}
</when>
<!--若所有的when条件都不满足,则执行otherwise条件-->
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
8、sql include标签的使用
抽取sql重用
避免使用select * from employee
会降低查询效率
<sql id="BaseSql">id,emp_name,emp_gender,email,birthday,updatetime,createtime</sql>
<select id="getAllEmployee" resultType="org.lc.entity.Employee">
select <include refid="BaseSql"/>
from employee
</select>
9、模糊查询
<if test="empName!=null and empName!=''">
and emp_name like concat('%',#{employee.name},'%')
</if>
10、el常用判断表达式
在Mybatis中你的SQL语句中不能直接写大于或者小于等和xml标签冲突的特殊符号,需要进行转义:
< | < | 小于号 |
---|---|---|
> | > | 大于号 |
& | & | 和 |
' | ' | 单引号 |
" | " | 双引号 |
<> | <> | 不等于 |
>= | >= | 大于等于 |
<= | <= | 小于等于 |
create_date_time >= #{startTime} and create_date_time <= #{endTime}
或者直接使用
<![CDATA[ ]]>
将所有冲突的特殊符号放在其中<if test="startTime != null "> AND <![CDATA[ order_date >= #{startTime,jdbcType=DATE} ]]> </if> <if test="endTime != null "> AND order_date <![CDATA[ <= ]]> #{endTime,jdbcType=DATE} </if>
在动态sql中,例如where if标签中判断参数:
gt | 大于 | > |
---|---|---|
gte | 大于等于 | >= |
eq | 是否等于 | == |
neq | 不等于 | != |
lt | 小于 | < (在条件判断的时候,test不能包含 '<' 字符) |
lte | 小于等于 | <= (在条件判断的时候,test不能包含 '<' 字符) |
11、_parameter的使用
单个参数(基本数据类型/包装类型/String)
单个参数可以不用取别名
可以使用
<if test="_parameter != null and _parameter!=''">
,
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="Java.lang.Integer" > select <include refid="Base_Column_List" /> from base.tb_user <if test="_parameter != null"> where id = #{id,jdbcType=INTEGER} </if> </select>
多个参数
使用
_parameter.get("0")
获取第一个参数,_parameter.get("0").属性
获取对象的属性值
List<User> select(User user,Page page)
<!--(通过parameter.get(0)得到第一个参数即user);-->
<if test='_parameter.get("0").name != null'>
<!--通过0.name确保第一个参数user的name属性值-->
name=#{0.name}
</if>
判断map参数的键是否存在
_parameter.containsKey('键')
12、map作为参数取值
数据:
Map<String, Object> map=new HashMap<>(); List<String> list=new ArrayList<>(); list.add("1189389726308478977"); list.add("1189390295668469762"); list.add("1189426437876985857"); map.put("ids", list); map.put("firstId", "1189389726308478977"); List<EduTeacher> list1 = mapper.test5(map); System.out.println(list1);
//可以不用为params取别名,因为可以直接通过键值取值 List<EduTeacher> test5(Map<String, Object> params);
直接使用
#{键}
获取指定的键值<select id="test5" resultType="org.lc.domain.entity.EduTeacher" parameterType="map"> select * from edu_teacher where id=#{firstId} </select>
在foreach标签中
collection="键"
获取键值<select id="test5" resultType="org.lc.domain.entity.EduTeacher" parameterType="map"> select * from edu_teacher where id in <foreach collection="ids" separator="," open="(" close=")" item="id"> #{id} </foreach> </select>
或者使用
collection="_parameter['键']"
获取键值<select id="test5" resultType="org.lc.domain.entity.EduTeacher" parameterType="list"> select * from edu_teacher where id in <foreach collection="_parameter['ids']" separator="," open="(" close=")" item="id"> #{id} </foreach> </select>
使用
_parameter['键']"
取map中的list并判断list大小<if test="_parameter['themeList']!=null and _parameter['themeList'].length>0"> and THEME_NO in <foreach collection="_parameter['themeList']" open="(" close=")" item="themeNo" separator=","> #{themeNo} </foreach> </if>
14、if test
判断字符串和字符
判断不为
null
<if test="name!=null">
判断不为空串
<if test="name!=null and name!=''">
<if test="name!=null and !name.equals('')">
<if test="name!=null and name.trim()!=''">
去除空格后判断判断单个字符
<if test="gender!=null and gender=='1'">
❌不生效原因是:mybatis是用OGNL表达式来解析的,在OGNL的表达式中,'1'会被解析成字符,java是强类型的,char 和 一个string 会导致不等,所以if标签中的sql不会被解析。
解决方法如下:
<if test='gender!=null and gender=="1"'>
单引号中加双引号<if test="gender!=null and gender=='1'.toString()">
使用toString()
将字符
转为字符串
判断Integr
不能使用 startIndex!='' ,因为Integer类型永远不会和字符相等
<if test="startIndex!=null and startIndex==1">
⚠️ 注意:判断相等的时候必须用 == 即双等号,如果用单等号<if test="appClassId=-1">
,则会导致参数appClassId变为赋值语句,即永远为-1
判断集合
<if test='neIds!=null and neIds.size()!=0 and neIds.get(0)!="-1" and neIds.get(0)!=""'>
15、MyBatis Log插件使用生成样例
2021-12-02 18:29:35 [java.sql.PreparedStatement]-[DEBUG] ==> Preparing: select * from ....
2021-12-02 18:29:35 [java.sql.PreparedStatement]-[DEBUG] ==> Parameters: 2021-11-01 00:00:00(String) ...
四、缓存机制
MyBatis缓存机制:将缓存数据放在Map(HashMap)集合中;能保存查询一些数据
一级缓存:线程级别的缓存;本地缓存,SqlSession级别的缓存
二级缓存:全局范围的缓存,除过当前线程;SqSession能用外其它可以使用
1、一级缓存
SqlSession级别的缓存;默认存在
机制:只要之前查询过的数据,mybatis就会保存在一个缓存中(Map);下次获取直接从缓存中拿。
- 只有在当前SqlSession内查询缓存才有效,关闭当前的SqlSession则缓存也清空
1)缓存失效的情况
①不同的SqlSession,使用不同的一级缓存
- 只有同一个SqlSession期间查询到的数据会保存在这个SqlSession的缓存中
- 下次使用这个SqlSession查询会从中拿(前提是之前的SqlSessin未关闭)
②同一个方法,不同的对象参数
- 由于之前可能没查该对象,所以需要再查一遍
③在这个SqlSession期间只要在两次查询之间执行了任何一次增删改操作,缓存也会清空
④手动清空当前SqlSession的缓存
sqlSession.clearCache()
2)基本使用
①相同的SqlSession从缓存中查询
@Test
public void Test10(){
SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
EmployeeDao mapper1 = sqlSession1.getMapper(EmployeeDao.class);
Employee Employee1 = mapper1.getEmployeeById(1);
Employee Employee2 = mapper1.getEmployeeById(1);
System.out.println(Employee1);
System.out.println(Employee2);
sqlSession1.close();
}
②不同的SqlSession缓存不同
@Test
public void Test10(){
SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
EmployeeDao mapper1 = sqlSession1.getMapper(EmployeeDao.class);
Employee Employee1 = mapper1.getEmployeeById(1);
System.out.println(Employee1);
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
EmployeeDao mapper2 = sqlSession2.getMapper(EmployeeDao.class);
Employee Employee3 = mapper2.getEmployeeById(1);
System.out.println(Employee3);
sqlSession2.close();
}
2、二级缓存
- 当SqlSession关闭或者提交后,一级缓存的数据会放在二级缓存中
- 使用二级缓存的对象必须实现序列化seralizable接口
1)配置流程
①需要手动开启二级缓存。在mybatis-cofig.xml
配置
<!--这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。-->
<settings>
<!--全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。-->
<setting name="cacheEnabled" value="true"/>
</settings>
②使某一个Dao.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="org.lc.dao.EmployeeDao">
<cache></cache>
<select id="getEmployeeById" resultType="Employee">
select * from employee where id=#{id};
</select>
</mapper>
2)基本使用
不同的SqlSession从二级缓存中拿数据
@Test
public void Test10(){
SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
EmployeeDao mapper1 = sqlSession1.getMapper(EmployeeDao.class);
Employee Employee1 = mapper1.getEmployeeById(1);
System.out.println(Employee1);
//必须关闭或者提交才能放入二级缓存
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
EmployeeDao mapper2 = sqlSession2.getMapper(EmployeeDao.class);
Employee Employee3 = mapper2.getEmployeeById(1);
System.out.println(Employee3);
sqlSession2.close();
}
3、缓存查询顺序
先查二级缓存,再查一级缓存,最后查数据库
4、缓存相关属性
1)全局setting的cacheEnable:
- 配置二级缓存的开关,一级缓存默认一直时打开的
2)select标签的useCache属性
配置这个select是否使用二级缓存。一级缓存一直都是使用的
<select id="getEmployeeById" resultType="Employee" useCache="false"> select * from employee where id=#{id}; </select>
3)sql标签的flushCache属性
- 增删改默认flushCache=true,。sq执行后,会同时清空一级和二级缓存
- 查询默认flushCache=false
4)sqlSesseion.clearCache()
- 只是用来清除一级缓存
五、PageHelper分页
导包
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.8</version>
</dependency>
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>
<!--配置mybatis分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
</configuration>
完整实例
自定义分页实体
@Getter
@Setter
public class ResponsePage {
/**
* 总页数
*/
private Long total;
/**
* 查询的数据
*/
private List<?> data;
public static ResponsePage build(){
return new ResponsePage();
}
public ResponsePage setTotal(Long total) {
this.total = total;
return this;
}
public ResponsePage setData(List<?> data) {
this.data = data;
return this;
}
}
主要方法参数:
- 获取查询结果
pageNum: 传入的页面
pageSize:每页的大小
PageHelper.startPage(int pageNum, int pageSize);
注意: 执行该分页代码后面必须紧跟查询方法,否则分页失效
- 获取查询的详细参数
T: 查询的对象类型
list:查询到的对象集合
PageInfo<T> employeePageInfo=new PageInfo<>(List<T> list);
@GetMapping("/getAll")
public ResponsePage getAllEmployee(@RequestParam(value = "page", defaultValue = "1") Integer page, @RequestParam(value = "size", defaultValue = "10") Integer size) {
//startPage(int pageNum, int pageSize)
//参数: 页码,每页的大小
PageHelper.startPage(page, size);
//必须紧跟查询操作,否则分页无效
List<Employee> allEmployee = employeeService.getAllEmployee();
//将当前查询到的数据传入PageInfo中,得到详细参数
PageInfo<Employee> employeePageInfo=new PageInfo<>(allEmployee);
System.out.println("当前页码:"+employeePageInfo.getPageNum());
System.out.println("总页码:"+employeePageInfo.getPages());
System.out.println("总记录数:"+employeePageInfo.getTotal());
System.out.println("当前页查询到的记录数:"+employeePageInfo.getSize());
System.out.println("当前页大小:"+employeePageInfo.getPageSize());
System.out.println("上一页:"+employeePageInfo.getPrePage());
System.out.println("当前页查询的结果:"+employeePageInfo.getList());
return new ResponsePage().setData(allEmployee).setTotal(employeePageInfo.getTotal());
}
六、逆向工程
官网参考 http://mybatis.org/generator/configreference/xmlconfig.html
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!--mybatis逆向工程-->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
核心xml文件
将mbg.xml该文件置于项目的根目录下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--官网参考-->
<!--http://mybatis.org/generator/configreference/xmlconfig.html-->
<!--将该配置文件置于项目的根目录位置-->
<!--targetRuntime: 生成sql的复杂度-->
<!--targetRuntime:MyBatis3 标准的(多条件)-->
<!--targetRuntime:MyBatis3Simple 简单的-->
<!--targetRuntime:MyBatis3DynamicSql 动态sql-->
<context id="default" targetRuntime="MyBatis3">
<!--连接哪个数据库-->
<!--指定数据库驱动,连接地址,用户名,密码-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/test"
userId="root"
password="123456">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--生成pojo-->
<!--targetPackage:目标包 targetProject:目标项目 (.\src目标项目的src下 ) -->
<javaModelGenerator targetPackage="org.mbg.eniity" targetProject=".\src\main\java\">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--sql映射文件位置,指定xml生成位置-->
<!---->
<sqlMapGenerator targetPackage="org.mbg.dao" targetProject=".\src\main\resources\">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!--dao接口-->
<javaClientGenerator type="XMLMAPPER" targetPackage="org.mbg.dao" targetProject=".\src\main\java\">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!--指定生成的表-->
<!--tableName表名 -->
<!--domainObjectName:表对应的对象名-->
<table tableName="hr" domainObjectName="Hr"></table>
<table tableName="hr_role" domainObjectName="HrRole"></table>
<table tableName="role" domainObjectName="Role"></table>
</context>
</generatorConfiguration>
生成执行代码
public class MyBatisGeneratorTest {
public static void main(String[] args) throws IOException, XMLParserException, InvalidConfigurationException, SQLException, InterruptedException {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
//指定配置文件的路径
File configFile = new File("mbg.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
}