文章目录
一、MyBatis简介
是一个基于java的持久层框架,提供的持久层框架包括SQL Maps和Data Access Object(Dao)
1.MyBatis特性
- MyBatis是一个持久层框架,支持定制化SQL、存储过程以及高级映射
- 避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
- 可以使用简单的XML(将sql语句写到xml中)或注解用于配置和原始映射,将接口和java的pojo映射成数据库中的记录
- 是一个半自动的ORM框架
2.MyBatis和其他持久化层技术对比
- JDBC
- SQL夹杂在java代码中,耦合度搞,导致硬编码内伤
- 维护不易,频繁修改的情况多见
- 代码冗长,开发效率低
- Hibernate和JPA
- 操作简便,效率高
- 程序中的长难sql需要绕过框架
- 内部自动生成的sql不容做特殊优化
- 基于全映射的全自动框架,大量字段的pojo进行部分映射时比较困难
- 反射操作太多,导致数据库性能下降
- MyBatis
- 轻量级、性能出色
- SQL和java代码分开,java代码专注业务,sql语句专注数据
二、搭建MyBatis
1.1、创建Maven项目
修改mavaen的仓库和配置文件
1.2、导入依赖
mybatis、junit、mysql驱动
<dependencies>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
</dependencies>
1.3、创建MyBatis的配置文件
两种配置文件:
- 核心配置文件:用于配置连接数据库的环境以及MyBatis的全局配置信息
- 映射文件:用来写SQL语句
习惯上命名为mybatis-config.xml,并非强制要求(整合ssm之后,可以省略)
核心配置文件
在main中的resource下创建mybatis-config.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>
<!--environments 配置连接数据库的文件-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/><!--事务管理类型-->
<dataSource type="POOLED"><!--POOLED 表示使用数据库连接池-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--mappers 引入映射文件-->
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
映射文件
看下面创建mapper接口中的映射文件
相关概念:ORM(Object Relationship Mapping)对象关系映射
- 对象:java的实体类对象
- 关系:关系型数据库
- 映射:两者之间的对应关系
映射文件的命名规则:表对应的实体类的类名+Mapper.xml
例如:表t_uer对应的实体类为User,他的映射文件的名字为:UserMapper.xml
映射文件的namespace属性的内容要和接口的全类名一致
映射文件中sql语句的id要和mapper接口中的方法名一样
1.4、创建mapper接口
mapper接口相当于原来的Dao,里面是操作数据库的方法。区别在于mapper仅仅是接口,不需要提供实现类
1)创建表
CREATE TABLE `t_user`(
username VARCHAR(20) NOT NULL PRIMARY KEY,
`password` VARCHAR(20)
)
2)创建pojo类
属性、有参、无参构造器、set、get方法、toString方法
public class User {
private String username;
private String password;
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
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 User(String username, String password) {
this.username = username;
this.password = password;
}
public User() {
}
}
3)创建接口(替代原来的Dao)
//代替原来的Dao,mybatis会调用接口中的方法
public interface UserMapper {
int addUser();//添加用户信息的抽象方法
}
4)创建映射文件
看1.3的映射文件创建要求
<?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="love.junqing.mapper.UserMapper"><!--namespace:对应mapper接口的全类名-->
<insert id="addUser"><!--sql语句的id要和方法名一致-->
<!--具体执行的sql语句-->
insert into `t_user` values("yfj","123456")
</insert>
</mapper>
5)引入映射文件
核心配置文件中引入
<!--mappers 引入映射文件-->
<mappers>
<mapper resource="UserMapper.xml"/><!--以resource为根目录-->
</mappers>
1.5、测试功能
创建一个类进行测试
public class FirstTest {
@Test
public void testadd() throws IOException {
//1.加载核心配置文件(Resource是ibatis.io下面的)
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");//核心配置文件名
//2.获取SqlSessionFactorBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();//获取构建对象
//3.通过构建对象获取SqlSessionFAcotry
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
//4.获取sqlSession会话对象
SqlSession sqlSession = build.openSession();
//5.获取mapper接口对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);//返回接口的实现类(使用了代理模式)
//6.测试功能
int i = mapper.addUser();//调用方法,返回受影响的行数
//7.因为核心配置文件中开启了事务,所以需要手动提交
sqlSession.commit();
System.out.println(i);
}
}
1.6、功能优化
1.自动提交事务
sqlsession默认不自动提交事务如果获取sqlsession对象的时候,设置为true,就不需要手动提交事务
//4.获取sqlSession会话对象
SqlSession sqlSession = build.openSession(true);
2.加入log4j日志功能
-
加入log4j的依赖
<!-- log4j日志 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
加入log4j的配置文件
文件名固定:log4j.xml
存放的位置:src/main/resources
日志的级别:FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)
从左到右打印的内容越来越详细
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"><!--爆红也没关系--> <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender"> <param name="Encoding" value="UTF-8" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" /> </layout> </appender> <logger name="java.sql"> <level value="debug" /> </logger> <logger name="org.apache.ibatis"> <level value="info" /> </logger> <root> <level value="debug" /> <appender-ref ref="STDOUT" /> </root> </log4j:configuration>
3.添加增加和修改功能
-
在接口中添加抽象方法
public interface UserMapper { int addUser(); int delUser(); int updateUser(); }
-
在映射文件中添加对应的标签和语句
<!-- int delUser();--> <delete id="delUser"> delete from `t_user` where username="yfj" </delete> <!-- int updateUser();--> <update id="updateUser"> update `t_user` set password="456" where username="zyj" </update>
-
测试相关方法
@Test public void testdel() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream); SqlSession sqlSession = build.openSession(true); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.delUser(); } @Test public void testupd() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream); SqlSession sqlSession = build.openSession(true); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.updateUser(); }
4.添加查询功能
首先确定查询出来的数据需要转化为什么类型(对象还是集合)
查询功能的标签必须设置resultType或者resultMap,从而说明结果类型
- resulType:设置默认的映射关系
- resultMap:设置自定义的映射关系(字段名一致赋值,字段名不一致则不赋值)
-
接口中添加方法
//查询一条信息 User selectUser(); //查询所有信息 List<User> allUser();
-
映射文件的标签中**需要添加resultType或者resultMap**
<!-- //查询一条信息--> <!-- User selectUser();--> <select id="selectUser" resultType="love.junqing.pojo.User"> select * from t_user where password="456" </select> <!-- //查询所有信息--> <!-- List<User> allUser();--> <select id="allUser" resultType="love.junqing.pojo.User"> select * from t_user </select>
-
测试方法
@Test public void testselect() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");//获取字节输入流 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();//获取工厂构建对象 SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream); SqlSession sqlSession = build.openSession(true); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.selectUser(); System.out.println(user); } @Test public void testselectAll() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream); SqlSession sqlSession = build.openSession(true); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.allUser(); for (User user:users){ System.out.println(user); } }
三、核心配置文件详解(了解即可)
1.environment
environments:用来配置多个数据库的环境
dafault:设置默认使用的环境id
environment:配置某个具体的环境
id:表示所连接数据库环境的唯一标识(不能重复)
transactionManager:表示事务管理方式(type属性有两个值)
- JDBC:表示当前环境中,执行sql时,使用的是jdbc中原生的事务管理方式
- MANAGED:被管理(例如Spring)
dataSource:配置数据源(type属性有三个值)
- POOLED:表示使用数据库连接池
- UNPOOLED:表示不适用数据库连接池
- JNDI:表示使用上下文中的数据源
2.properties
把数据库的信息放到配置文件中
-
properties配置文件
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis jdbc.username=root jdbc.password=123456
-
引入properties文件
<properties resource="jdbc.properties"></properties>
-
将信息写入
<property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/>
3.typeAliases
用来设置类型别名:应为接口的配置文件中,查询的时候resultType里面的包路径很长
typeAliases标签要放到properties标签后面
<typeAliases>
<typeAlias type="love.junqing.pojo.User" alias="User"></typeAlias>
</typeAliases>
-
type是全路径
-
alias是别名(可以设置,可以不设置)
-
设置了之后,值就是设置的;若没有设置,就是类名(不区分大小写)
-
设置完了之后,接口的配置文件的resultType可以使用别名
-
一般使用下面这种方式,使用package标签,他的name设置包名,这样就将包下面的所有类都设置了别名(类名不区分大小写)
<typeAliases> <package name="love.junqing.pojo.User"/> </typeAliases>
4.mappers
批量引入映射文件
可以使用package标签,直接把一整个包弄进来
使用要求:
package的路径使用 .
mapper接口所在的包要和映射文件所在的包名一致
mapper接口的配置文件在放在resources中(创建包的时候使用 / 不用 . )
mapper接口要和映射文件的名字一致
<mappers>
<package name="love.junqing.mapper"/>
</mappers>
四、IDEA设置核心配置文件模板
-
创建mybatis的核心配置文件
<?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> <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> <mappers> <mapper resource="UserMapper.xml"/> </mappers> </configuration>
-
创建一个properties配置文件
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis jdbc.username=root jdbc.password=123456
-
核心配置文件中引入properties配置文件
<properties resource="jdbc.properties"></properties>
-
核心配置文件引用配置文件中的值
<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>
-
设置类型别名(如果有需要的话)
-
设置映射文件
-
找到idea的Setting–>Editor–>File and Code–>点击加号,设置name为mybaits-config,后缀为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> <properties resource="jdbc.properties"></properties> <typeAliases> <package name=""/><!--类型别名--> </typeAliases> <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> <mappers> <package name=""/><!--映射文件所在的包,需要和接口的包名一致--> </mappers> </configuration>
五、IDEA设置映射文件模板
根据功能进行划分
-
根据上面四的路径,找到创建模板路径,粘贴进去
<?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=""><!--namespace:对应接口的全类名--> </mapper>
六、将获取SqlSession抽取为工具类
public class SqlUtils {
public static SqlSession getSqlSession(){
SqlSession sqlSession=null;
try {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
sqlSession = build.openSession(true);
} catch (IOException e) {
e.printStackTrace();
}
return sqlSession;
}
}
七、MyBatis获取参数值的两种方式(重点)
Mybatis获取参数值的两种方式:${ }和#{ }
- ${ }:本质就是字符串拼接(会造成sql注入)
- #{ }:本质就是占位符赋值
1.单个字面类型的参数
可以通过${ }和#{ }以任意的名称获取参数值
但是需要注意${ }需要使用单引号引起来
1.${ }
-
Mapper接口
User getUserByUsername(String username);
-
映射文件
<select id="getUserByUsername" resultType="User"> select * from t_user where username='${username}'<!--如果使用${}则需要加上单引号,里面的参数名随便,只是起一个占位置的作用--> </select>
2.#{ }
-
映射文件
select * from t_user where username= #{username}<!--#{}不需要单引号-->
2.多个字面类型的参数
如果传入多个参数,mabatis会把参数放到一个map中,以两种方式存储
- 以arg0,arg1…为键,以参数为值
- 以param1,param2…为键,以参数位为值
可以使用${ }或#{ }以键的方式访问值
select * from t_user where username= #{arg0} and password=#{param2}<!--arg从0开始,param从1开始,可以混着用-->
3.map集合类型的参数
如果mapper接口方法的参数有多个的时候,可以手动将参数放到map集合中,然后将集合设置为查询的参数
-
mapper接口
User getUserByUsername(Map map);//查询的参数以集合的形式传入
-
映射文件
<select id="getUserByUsername" resultType="User"> select * from t_user where username= #{username} and password=#{password}<!--查询的时候使用的是我们自己定义的键--> </select>
-
测试
Map<String, Object> map = new HashMap<>(); map.put("username","zyj"); map.put("password","456"); User userByUsername = mapper.getUserByUsername(map); System.out.println(userByUsername);
4.对象类型的参数
如果传入的是一个对象类型的参数,可以通过属性名获取值
- 这里sql语句中的参数不是对象的属性,而是对象中set和get方法去掉set或者get的名字
-
mapper接口
int addUser(User user);
-
映射文件(里面的参数是get或者set方法去掉set或者get得到的)
<insert id="addUser"> insert into t_user values(#{username},#{password})<!--通过get或者set方法找到值--> </insert>
-
测试
Integer i = mapper.addUser(new User("yfj","123456"));
5.使用@Param命名参数
传入多个参数的时候,mybatis会自动将我们的参数放入一个map集合中,但是名字是arg,不是我们自定义的。
当我们使用@Param后,就可以通过自己设置的键访问(也可以使用param1,parma2访问)
-
mapper接口
User getUserByUsernameAndP(@Param("username") String username,@Param("password")String password);
-
映射文件
<select id="getUserByUsernameAndP" resultType="User"> select * from t_user where username= #{username} and password=#{password}<!--查询的时候使用的是我们自己定义的键--> </select>
-
测试
User yfj = mapper.getUserByUsernameAndP("yfj", "123456");
八、Myabtis的各种查询功能
- 如果查询出来的对象只有一条,可以通过实体类接收对象,也可以通过list集合接收
- 如果查询出的是数据有多条,不能通过实体类对象来接收,因为会报异常,可以使用list接收对象集合
- 如果查询出来的数据没有对应的实体类,可以用map接收
1.查询结果是一个实体类对象
Myabatis中设置了默认的类型别名
- java.lang.Integer–>int,Integer
- int–>_int, _integer
- Map–>map
- String—>string
-
用实体类接收
-
mapper接口
User getUserByUsername(Map map);
-
映射文件
<select id="getUserByUsername" resultType="User"> select * from t_user where username= #{username} and password=#{password}<!--查询的时候使用的是我们自己定义的键--> </select>
-
-
用list集合接收一个实体类
-
mapper接口
List getUserToList(@Param("username") String uername);
-
映射文件
<select id="getUserToList" resultType="User"> select * from t_user where username= #{username} </select>
-
2.查询结果是一个Map类型
如果查询结果的返回类型没有实体类可以接受,那么可以将结果放到Map中,以键值对的形式放到map集合中
-
mapper接口
Map<String,Object> getUserToMap(@Param("username") String uername);
-
映射文件
<select id="getUserToMap" resultType="Map"> select * from t_user where username= #{username} </select>
-
测试方法
Map yfj = mapper.getUserToMap("yfj"); System.out.println(yfj);
3.查询结果是多个对象
-
查询结果是多个实体类对象
就是平常的方法
-
查询结果是多个map集合
有两个方法:
- 可以是使用List存储map集合,也就是下面给的案例
- 也可以方法的返回类型就是Map,但是在方法上加一个@MapKey()注解,注解里面的值是一个键,键的值不会 重复,那么就会把这个独一无二的一个键作为map的键
-
mapper接口
List<Map<String,Object>> getUsersToMap();
-
映射文件
<select id="getUsersToMap" resultType="map"> select * from t_user </select>
-
测试方法
List<Map<String, Object>> usersToMap = mapper.getUsersToMap(); System.out.println(usersToMap);
九、特殊SQL的执行
某些sql语句使用#{ }会出现问题,所以就需要使用${ }查询
1.模糊查询(3种方法)
(1)${ }方法
select * from t_user where username like '%${username}%' <!--% 表示匹配任意多个字符,${} 里面是传入的参数-->
(2)concat的字符串拼接
select * from t_user where username like concat('%',#{username},'%') <!--三部分拼接到一起,#{}里面是传入的参数-->
(3)双引号直接拼接(最常用)
select * from t_user where username like "%"#{username}"%" <!--使用的是双引号“ ”-->
2.批量删除
<delete id="deletesome">
delete from t_user where id in (${ids})
</delete>
3.动态设置表名
<select id="getUserToList" resultType="User">
select * from ${tableNAme} <!--表名是传进来的-->
</select>
4.获取自增的主键
案例:user对象的id属性是自动增长的,当我们执行一个insert语句把user对象添加进去的同时想要获取user对象的id属性
- useGeneratedKeys:表示当前标签中的sql使用了自增的id
- keyProperty:从sql获取到的自动增长的属性值给java对象的哪个属性
-
映射文件
<insert id="addUser" useGeneratedKeys="true" keyProperty="id"> insert into t_user values(null,#{username},#{password})<!--将获取到的自增id,赋给java对象的id属性--> </insert>
-
测试方法
User uer=new User(null,"yfj","123");//创建了一个user对象,这个时候user对象是没有id值的 mapper.addUser(user);//执行sql System.out.println(user);//这个时候user对象的id有了值
十、解决字段名不一致(3种方法)
字段名和属性名不一致,的处理方法
例如:mysql字段名带下划线,java类中不带下划线
处理方法(两种):
- sql语句中设置别名 select user_name username,emp_age age …
- 使用Myabtis的全局配置
- 使用resultMap
方法一:sql语句中使用别名
select user_name username,emp_age age from t_user
方法二:设置Mybatis全局配置
放在properties标签下面
将下划线自动映射成驼峰 user_name -->userName
<settings>
<!--将下划线自动映射成驼峰 user_name -》userName -->
<setting name="mapUnderscoreToCamelCase" value="true"/><!--true表示开启-->
</settings>
方法三:使用resultMap
设置resultMap标签:id属性是唯一标识,就是select标签resultMap的值
type属性的值就是对应的实体类类型
id标签用于主键的关系:property是java对象的属性名,column是sql中的字段名
result标签用于普通字段的关系:property是java对象的属性名,column是sql中的字段名
使用resultMap需要把所有的属性都设置
<!--type属性的值就是对应的实体类名-->
<resultMap id="UserResultMap" type="User">
<!--property是java对象的属性名,column是sql中的字段名-->
<id property="username" column="user_name"></id>
<result property="password" column="password"></result>
<result property="sex" column="t_sex"></result>
</resultMap>
<!--resultMap="UserResultMap" 表示使用上面的resultMap-->
<select id="allUser" resultMap="UserResultMap">
select * from t_user
</select>
十一、resultMap的功能
1.resultMap的功能
- 解决字段名和对象属性名不一致的情况(看上面的笔记)
- 解决多对一的映射关系
- 解决一对多的映射关系
2.多对一的映射关系(3种方式)
操作背景:查询员工的信息及所属部门的信息
出现问题:员工类没有对应的属性去接收部门的信息,所以需要resultMap自定义映射信息
所有方法都需要的公共部分
-
在多的pojo中创建一的对象
例如:一个部门对应多个员工,就在员工类中创建部门对象
private Dept dept;
-
设置get和set方法
public Dept getDept() { return dept; } public void setDept(Dept dept) { this.dept = dept; }
-
修改构造器和toString方法
a>方法一:级联属性赋值(resultMap映射到属性对象的属性中)
<resultMap id="empDeptMap" type="Emp">
<id column="eid" property="eid"></id>
<result column="ename" property="ename"></result>
<result column="age" property="age"></result>
<result column="sex" property="sex"></result>
<result column="did" property="dept.did"></result>
<result column="dname" property="dept.dname"></result>
</resultMap>
<!--Emp getEmpAndDeptByEid(@Param("eid") int eid);-->
<select id="getEmpAndDeptByEid" resultMap="empDeptMap">
select emp.*,dept.* from t_emp emp left join t_dept dept on emp.did =
dept.did where emp.eid = #{eid}
</select>
b>association处理映射关系
- 首先把有对应关系的写好
- 然后使用association标签,给属性对象赋值
- association标签中
- property:表示对象中的属性名
- javaType:表示这一部分对应的对象类型
- 然后再association里面在使用id、reult标签
<resultMap id="empDeptMap" type="Emp">
<id column="eid" property="eid"></id>
<result column="ename" property="ename"></result>
<result column="age" property="age"></result>
<result column="sex" property="sex"></result>
<!--给属性对象赋值-->
<association property="dept" javaType="Dept"><!--property:表示Emp对象中的属性名 javaType:表示这个属性对应的对象类型-->
<id column="did" property="did"></id>
<result column="dname" property="dname"></result>
</association>
</resultMap>
<!--Emp getEmpAndDeptByEid(@Param("eid") int eid);-->
<select id="getEmpAndDeptByEid" resultMap="empDeptMap">
select emp.*,dept.* from t_emp emp left join t_dept dept on emp.did =
dept.did where emp.eid = #{eid}
</select>
c>分步查询(重要)
也是使用association,但是是使用多条sql语句查询
先查询能直接查到的,然后利用查到的,去查另外查不到的部分
例如:需要查询员工和他的部门信息
先查询员工的信息,然后利用查询出来的所属部门,去查询部门
分布查询优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息(settings标签)
- lazyLoadingEnable:延迟加载的全局开关,开启时,所有关联对象都会延迟加载(所以需要开启)
- aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性(所以需要关闭)
延迟加载
需要访问哪些信息,就会去加载,没有访问的就不会去加载
<settings>
<setting name="lazyLoadingEnable" value="true"/>
</settings>
- 如果有些sql语句不需要延迟加载可以设置association标签的fetchType属性(lazy为延迟加载,eager为立即加载)
-
第一步查询员工信息
-
mapper接口
Emp getEmpByStep(@Param("eid") int eid);
-
映射文件
<resultMap id="empDeptStepMap" type="Emp"> <id column="eid" property="eid"></id> <result column="ename" property="ename"></result> <result column="age" property="age"></result> <result column="sex" property="sex"></result> <!--select:第二步查询的方法全路径(mapper接口名.方法名)--> <association property="dept" select="com.atguigu.MyBatis.mapper.DeptMapper.getEmpDeptByStep" column="did"><!--column:将查询结果中的某个字段设置为分步查询的条件--> </association> </resultMap> <!--Emp getEmpByStep(@Param("eid") int eid);--> <select id="getEmpByStep" resultMap="empDeptStepMap"> select * from t_emp where eid = #{eid} </select>
-
-
第二步查询部门信息
-
mapper接口
Dept getEmpDeptByStep(@Param("did") int did);
-
映射文件
<!--Dept getEmpDeptByStep(@Param("did") int did);--> <select id="getEmpDeptByStep" resultType="Dept"> select * from t_dept where did = #{did} </select>
-
3.一对多的映射关系(2种)
一对多公共部分
在一的pojo中创建一的集合
例如:一个部门对应多个员工,就在部门类中创建员工集合,然后设置get、set、toString方法
private List<User> users;
(1)通过collection解决一对多
resultMap直接能查询出来的就是老步骤
resultMap中使用collection标签
- property:表示对象中的属性对象名(部门类中的员工对象名)
- ofType:集合中存放的是什么类型数据
注意:collection标签中不要再设置部门类了(否则会套娃)
-
mapper接口
Dept getDeptEmpByDid(@Param("did") int did);
-
映射文件
<resultMap id="deptEmpMap" type="Dept"> <id property="did" column="did"></id> <result property="dname" column="dname"></result> <!-- ofType:设置collection标签所处理的集合属性中存储数据的类型 --> <collection property="emps" ofType="Emp"> <id property="eid" column="eid"></id> <result property="ename" column="ename"></result> <result property="age" column="age"></result> <result property="sex" column="sex"></result> </collection> </resultMap> <select id="getDeptEmpByDid" resultMap="deptEmpMap"> select dept.*,emp.* from t_dept dept left join t_emp emp on dept.did = emp.did where dept.did = #{did} </select>
(2)通过分步解决一对多
-
第一步:只需要查询部门信息
-
mapper接口
Dept getDeptByStep(@Param("did") int did);
-
映射文件
<resultMap id="deptEmpStep" type="Dept"> <id property="did" column="did"></id> <result property="dname" column="dname"></result> <collection property="emps" fetchType="eager" select="com.atguigu.MyBatis.mapper.EmpMapper.getEmpListByDid" column="did"> </collection> </resultMap> <!--Dept getDeptByStep(@Param("did") int did);--> <select id="getDeptByStep" resultMap="deptEmpStep"> select * from t_dept where did = #{did} </select>
-
-
第二步:设置collectuion标签
<!--property:部门类种对应的用户对象名 fetchType:加载方式(立即加载、延迟加载) select:调用的查询方法全路径 column:给下一步的条件 --> <collection property="emps" fetchType="eager" select="com.atguigu.MyBatis.mapper.EmpMapper.getEmpListByDid" column="did"> </collection>
-
第三步:通过第二步给的条件查询用户信息
-
mapper接口
List<Emp> getEmpListByDid(@Param("did") int did);
-
映射文件
<select id="getEmpListByDid" resultType="Emp"> select * from t_emp where did = #{did} </select>
-
十二、动态SQL
存在的意义是为了拼接sql语句
主要用于:多条件查询(例如:淘宝条件的筛选)
1.if标签
可以根据test属性中的表达式决定是否拼接到sql中,如果传递过来的符合条件,就执行标签里面的
-
test属性:是判断的条件,如果返回对象中有这个属性,那么可以直接使用
-
如果有多个if标签,需要使用and 或者 or进行拼接
-
建议where后面加一个条件1=1,防止第一个if标签里面的条件不成里(缺点b)
<select id="allUser" resultType="User"> select * from t_user where 1=1 <if test="username!=null and username!='' "><!--里面的参数如果传进来的对象有,可以直接调用--> and username=#{username}<!--执行的条件,#号里面是传递进来的参数--> </if> <if test="password!=null and password!='' "> and password=#{password}<!--因为是和前面的条件进行拼接,所以需要使用and或者or--> </if> </select>
2.where标签
用来解决if标签出现的问题(不需要再自己加1=1的条件,同时会自动删除if标签中的and 、or)
-
当where标签中的条件成立时,会自动生成where关键字,并且将内容前多余的and 或 or 去掉
-
当where 标签中的条件都不成立时,此时where标签没有任何效果
-
注意:只能去掉内容前的多余and和or,不能去掉内容后的and和or
<select id="allUser" resultType="User"> select * from t_user <where> <if test="username !=null and username!='' "> username=#{username} </if> <if test="password !=null and password!='' "> and password=#{password} </if> </where> </select>
3.trim标签
用来解决where不能去掉内容后面and或or的问题
- prefix属性:在trim标签中的内容前****添加指定内容
- suffix属性:在trim标签中的内容后添加指定内容
- suffixOverrides属性:在trim标签中的内容后面去掉指定内容
- perfixOverrides属性:在trim标签中的内容前面去掉指定内容
- 说明:如果标签中没有内容,则trim标签没有任何效果
<select id="allUser" resultType="User">
select * from t_user
<trim prefix="where" suffixOverrides="and|or" ><!--因为缺少where所以前缀加一个where,后面去掉and或or-->
<if test="username !=null and username!='' ">
username=#{username} and
</if>
<if test="password !=null and password!='' ">
password=#{password}
</if>
</trim>
</select>
4.choose、when、otherwise标签
choose是父标签,when、otherwose都需要写在choose标签里面
相当于switch—case:choose表示switch,when表示case,otherwise表示default
when不会穿透
注意:when至少要有一个,otherwise最多有一个
<select id="allUser" resultType="User">
select * from t_user
<where>
<choose>
<when test="username !=null and username!='' ">
username=#{username}
</when>
<when test="password !=null and password!='' ">
password=#{password}
</when>
<otherwise><!--都不成立才会执行的操作-->
dept=#{dept}
</otherwise>
</choose>
</where>
</select>
5.foreach标签
用来实现批量删除和批量添加功能(因为传递进来的是一个List集合或者数组)
collection属性:就是传进来的数组名
item属性:表示数组中的元素(随便起个名字,只要#{}里面用他就行)
separator属性:表示分隔符
open属性:表示foreach以什么开始
close属性:表示foreach以什么结束
(1)通过数组实现批量删除
-
mapper接口
Integer deleteUser(@Param("usernames") String []usernames);
-
映射文件(使用in的方式)
<!-- Integer deleteUser(@Param("usernames") Integer []usernames);--> <delete id="deleteUser"> delete from t_user where username in <foreach collection="usernames" item="username" separator="," open="(" close=")"> #{username} </foreach> </delete>
-
映射文件(使用or的方式)
<!-- Integer deleteUser(@Param("usernames") Integer []usernames);--> <delete id="deleteUser"> delete from t_user where <foreach collection="usernames" item="username" separator="or" open="(" close=")"> username=#{username} </foreach> </delete>
(2)通过List集合实现批量添加
-
mapper接口
Integer addUsers(@Param("users") List<User> users);
-
映射文件
<!-- Integer addUsers(@Param("users") List<User> users);--> <insert id="addUsers"> insert into t_user values <foreach collection="users" item="user" separator=","> (#{user.username},#{user.password}) </foreach> </insert>
6.sql标签
可以将常用的字段存放起来,需要使用的时候直接引用
使用include标签引用,refid属性对应sql标签的id
<!--设置sql片段-->
<sql id="userinform">username,password</sql>
<!--引用sql片段-->
<select id="allUser" resultType="User">
select <include refid="userinform"></include>from t_user
</select>
十三、MyBatis的缓存
1.MyBatis的一级缓存
一级缓存默认开启,是SqlSession级别的,只对查询功能有效
SqlSession就是我们java代码获取mapper对象的那个sqlsession对象
只要我们使用的是同一个SqlSession对象,那么就会存在缓存,即使不使用相同的mapper对象
如果存在缓存,查询相同数据的时候,就不会再执行sql语句,而是直接从缓存中取
- 使一级缓存失效的四种情况:
- 不适用同一个SqlSession
- 使用同一个SqlSession,但是查询条件不同
- 使用同一个SqlSession进行了两次查询,但是中间执行了一个增删改操作
- 使用同一个SqlSession进行查询操作,但是手动清除了缓存(sqlSession的clearCache方法)
2.MyBatis的二级缓存
二级缓存是SqlSessionFactory级别的,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
- 二级缓存开启的必要条件:
- 在核心配置文件中,设置全局配置属性cacheEnabled=“true”(默认为true,不需要设置)
- 在映射文件中设置标签
<cache/>
- 二级缓存必须在SqlSession关闭或提交之后有效(调用sqlSession.close( )或者sqlSession.commit( ))
- 查询的数据所转换的实体类类型必须实现序列化的接口(需要手动去实现Serializable接口)
- 使二级缓存失效的情况:
- 两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
3.二级缓存的配置文件
在mapper配置文件中添加cache标签可以设置一些属性:
-
eviction属性:缓存回收策略
LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象【默认使用】。
FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU。
-
flushInterval属性:刷新间隔(二级缓存多长时间刷新一次),单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
-
size属性:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
-
readOnly属性:只读,true/false
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了 很重要的性能优势。
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。
4.MyBatis缓存查询的查询顺序
- 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
- 如果二级缓存没有命中,再查询一级缓存
- 如果一级缓存也没有命中,则查询数据库
- SqlSession关闭之后,一级缓存中的数据会写入二级缓存
5.整合第三方缓存EHCache
只能用第三方工具代替Myabtis的二级缓存
下面的只需要会配就行
说明:虽然使用的是第三方缓存,但是实现的功能还是一样的,依旧是SqlSessionFactory级别
(1)引入依赖
<!-- Mybatis EHCache整合包 -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
<!-- slf4j日志门面的一个具体实现 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
各jar包的功能:
mybatis-ehcache:Mybatis和EHCache的整合包
ehcache:EHCache核心包
slf4j-api:SLF4J日志门面包
logback-classic:支持SLF4J门面接口的一个具体实现
(2)创建EHCache的配置文件ehcache.xml
放在resources文件夹下
<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"><!--报错不用管-->
<!-- 磁盘保存路径 -->
<diskStore path="D:\atguigu\ehcache"/>
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
(3)设置二级缓存的类型
用在映射文件中
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
(4)加入logback日志
存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。 创建logback的配置文件logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置 -->
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger]
[%msg]%n</pattern>
</encoder>
</appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<root level="DEBUG">
<!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
<appender-ref ref="STDOUT" />
</root>
<!-- 根据特殊需求指定局部日志级别 -->
<logger name="com.atguigu.crowd.mapper" level="DEBUG"/>
</configuration>
十四、MyBatis的逆向工程
正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程 的。
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源: Java实体类、Mapper接口、Mapper映射文件
1.逆向工程的创建步骤(清新简洁版)
(1)添加依赖
<!-- 依赖MyBatis核心包 -->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<!-- 控制Maven在构建过程中相关配置 -->
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>
<!-- 插件的依赖 -->
<dependencies>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
(2)创建MyBatis的核心配置文件
(3)创建逆向工程的配置文件
文件名必须是:generatorConfig.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>
<!--targetRuntime: 执行生成的逆向工程的版本
MyBatis3Simple: 生成基本的CRUD(清新简洁版)
MyBatis3: 生成带条件的CRUD(奢华尊享版)
-->
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis"
userId="root"
password="123456">
</jdbcConnection>
<!-- javaBean的生成策略
targetPackage:bean生成在哪个包
targetProject:包生成在哪里-->
<javaModelGenerator targetPackage="com.junqing.mybatis.bean"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" /><!--是否自动生成包-->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.junqing.mybatis.mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.junqing.mybatis.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName:数据库中的表名-->
<!-- domainObjectName:生成的实体类的类名 -->
<!-- mapper接口和映射文件会根据domainObjectName自动生成 -->
<table tableName="t_user" domainObjectName="User"/>
</context>
</generatorConfiguration>
(4)使用插件执行功能
点击右侧Maven的plugins的mybatis-generator插件
2.逆向工程的创建步骤(奢华尊享版)
创建步骤一样,只需要把generatorConfig的标签中的targetRuntime属性的值改为MyBatis3即可
<?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>
<!--targetRuntime: 执行生成的逆向工程的版本
MyBatis3Simple: 生成基本的CRUD(清新简洁版)
MyBatis3: 生成带条件的CRUD(奢华尊享版)
-->
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis"
userId="root"
password="123456">
</jdbcConnection>
<!-- javaBean的生成策略
targetPackage:bean生成在哪个包
targetProject:包生成在哪里-->
<javaModelGenerator targetPackage="com.junqing.mybatis.bean"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" /><!--是否自动生成包-->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.junqing.mybatis.mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.junqing.mybatis.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName:数据库中的表名-->
<!-- domainObjectName:生成的实体类的类名 -->
<!-- mapper接口和映射文件会根据domainObjectName自动生成 -->
<table tableName="t_user" domainObjectName="User"/>
</context>
</generatorConfiguration>
使用说明:
需要给pojo对象写一下有参、无参构造器和toString方法
(1)奢华尊享版查询所有数据
使用selectByExample方法,传进一个null即可
public void testMBG(){
try {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//selectByExample:表示根据条件查询,如果没有条件就表示查询所有数据
List<User> users = mapper.selectByExample(null);
for (User user:users){
System.out.println(user);
}
} catch (IOException e) {
e.printStackTrace();
}
}
(2)奢华尊享版根据条件查询
- Example对象的createCriteria表示and,Example对象的or表示or
- createCriteria和or的方法都是以and打头的
- 一个Example对象是由若干createCriteria和or拼接起来的
public void testMBG(){
try {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
UserExample userExample = new UserExample();
//createCriteria:表示and连接条件
//or:表示or连接
//userExample条件是所有的createCriteria和or连接起来的,他们的方法都是and打头的
//例如下面的语句就是 password='123456' and username not null or username =yfj
userExample.createCriteria().andPasswordEqualTo("123456").andUsernameIsNotNull();
userExample.or().andUsernameEqualTo("yfj");
List<User> users = mapper.selectByExample(userExample);
for (User user:users){
System.out.println(user);
}
} catch (IOException e) {
e.printStackTrace();
}
}
(3)奢华尊享版修改
updateByPrimaryKey:是根据主键修改(传进null,就修改成null)
updateByPrimaryKeySelective:是根据主键选择性修改(如果传进null,就不会修改这个属性)
其他方法也一样:带Selective的就是选择性修改,不带Selective的就是全部修改(传进null,就改成null)
(4)奢华尊享版添加
其他方法也一样:带Selective的就是选择性添加,不带Selective的就是全部添加(传进null,就改成null)
(5)奢华尊享版删除
其他方法也一样:带Selective的就是选择性删除,不带Selective的就是全部删除(传进null,就改成null)
十五、分页插件
不需要写sql语句,只需要设置相应的信息,就可以实现分页
可以通过分页插件获取所有的分页数据
例如:当前页的上一页,当前页的下一页,是不是最后一页,当前页是第几页,是不是最后一页
1.分页插件使用步骤
(1)添加依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
(2)配置分页插件
在MyBatis的核心配置文件中配置(environments标签前面,typeAliases标签后面)
<plugins>
<!--设置分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
2.分页插件的使用
分页语句:limit index , pageSize
index:当前页的起始索引(pageNum-1)*pageSize
pageSize:每页显示的条数
pageNum:当前页的页码
使用MyBatis的分页插件实现分页功能:
需要在查询功能直线开启分页
PageHelper.startPage(当前页码,每页的条数)
在查询功能之后获取分页相关信息
PageInfo page=new PageInfo(users,5);
(1)开启分页功能
只需要在查询语句之前开启分页功能:PageHelper.startPage(当前页码,每页的条数)
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
PageHelper.startPage(1,4);//表示开启分页,第一个参数是当前页码,第二个参数是页面展示数据条数
UserExample userExample = new UserExample();
userExample.createCriteria().andPasswordEqualTo("123456").andUsernameIsNotNull();
userExample.or().andUsernameEqualTo("yfj");
List<User> users = mapper.selectByExample(userExample);
for (User user:users){
System.out.println(user);
}
(2)获取分页相关数据
在查询获取list集合之后,使用PageInfo<集合存放的数据类型> pageInfo = new PageInfo<>(List list, int navigatePages)获取分页相关数据
list:分页之后的数据
navigatePages:导航分页的页码数
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
PageHelper.startPage(2,5);//开启分页
UserExample userExample = new UserExample();
userExample.createCriteria().andPasswordEqualTo("123456").andUsernameIsNotNull();
userExample.or().andUsernameEqualTo("yfj");
List<User> users = mapper.selectByExample(userExample);
//PageInfo中的泛型是List中存放的类型
//参数:第一个是查询到的集合,第二个是展示的页码个数(类似于页面中展示的当前页面,和当前页面左右两边的页码)
PageInfo<User> page=new PageInfo<User>(users,5);
for (User user:users){
System.out.println(user);
}
(3)分页相关数据
PageInfo{
pageNum=8, pageSize=4, size=2, startRow=29, endRow=30, total=30, pages=8, list=Page{count=true, pageNum=8, pageSize=4, startRow=28, endRow=32, total=30, pages=8, reasonable=false, pageSizeZero=false}, prePage=7, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true, hasNextPage=false, navigatePages=5, navigateFirstPage4, navigateLastPage8, navigatepageNums=[4, 5, 6, 7, 8]
}
常用数据:
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]
十六、MyBatis快速总结
1.MyBatis的基本说明
1.核心配置文件用来存放数据库的url、username、password等信息,还存放映射文件的包路径。核心配置文件一般放在resources下,springboot中可以没有【因为都放在yaml文件中了】
2.在核心配置文件的mappers标签中使用package标签批量引入映射文件的时候,映射文件需要在reousrces下,包名需要和mapper接口的包路径一致
3.映射文件类似于原来的DaoImpl(mapper标签的namespace属性就是Mapper接口的全路径,增删改查都有对应的标签(标签的id属性就是接口中的方法名))
4.Mapper接口【需要加@Mapper注解】对应着原来的Dao,里面只定义方法,具体操作在映射文件中写
5.查询标签使用的时候需要确定查询出来的数据转化为什么类型,因此要设置resultType或者resultMap属性从而说明结果类型(值是对应类型的全路径)
6.resulType是默认的映射关系(查询出来的结果可以直接放到结果类型里面),resultMap是自定义映射类型(字段名和bean属性不一样的时候使用)
2.映射文件获取值的方法
映射文件获取参数的两种方式:${ }和#{ }
- ${ }:就是字符串拼接,所以存在SQL注入,所以使用的时候外面需要单引号【有些查询必须使用这个,例如:批量删除、动态设置表名、获取自增主键,具体看笔记】
- #{ }:就是占位符赋值,会自动加单引号
- 如果只传入一个参数,那么直接把参数名添加到sql中即可
- 如果传入的参数有多个,myabtis会自动封装到Map中,那么需要以arg0,arg1 … 或者 param0,param1 … 为键
- 因此如果有多个参数传入,我们可以手动将参数封装的到Map中,那么传入参数的时候,可以使用自己设置的键
- 如果传入的是一个实体类对象,那么获取参数的值是set和get方法名去掉set或者get后剩下的名字
- 如果mapper接口方法中的参数使用@Param注解,那么获取参数的时候就是Param注解的值【@Param可以自定义键名】
- 因此可以分为两种情况获取参数(上面几种都可以用,但是建议):如果传入的实体对象,直接使用去掉set后的名字,其他的使用@Param注解
3.查询结果
-
如果查询出的数据只有一条(行),可以通过实体类或者List集合接收
-
如果查询出的数据有多条,只能通过List集合接收,resultType的值还是单条数据的类型
例如:每一行可以封装到User类中,那么resultType的值就是User
-
如果查询出来的数据是单行单列的值,可以不用实体类接收,可以用java中常用的数据类型接收(如:int,String,List等),resultType中可以不用写全路径,直接用平时的写法就行
例如:int,String,BigDecimal,List,Map等
4.模糊查询的方法
模糊匹配有很多方法,可以使用${ }。但是最推荐的是用双引号把匹配符号引起来,然后使用#{ }
<!--例如:-->
select * from emp where username like "%"#{username}"%"
5.获取自动增长的主键
insert标签中有一个useGeneratedKeys属性设置为true,然后keyProperty属性设置需要将主键的值给哪个属性
keyProperty:将自增的主键的值赋给resultType中的哪个属性中
<!--例如:将数据插入到emp表,emp表的主键是id。要求插入完成后还需要获取主键-->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into emp values(null,#{username},#{age})
</insert>
6.解决sql字段和Bean的属性名不一致的方法(三种)
-
方法一:查询的sql语句为sql字段设置别名
select empname name ,emp_age age from emp
-
方法二:通过全局配置文件设置驼峰映射(
mapUnderscoreToCamelCase
属性),SpringBoot中也是这样 -
方法三:通过resultMap设置自定义映射关系
-
首先外面使用resultMap标签,标签的id属性随便,type属性是每一行查询结果对应的实体类
-
然后resultMap标签里面使用id标签设置主键映射关系,id标签中的property是实体类中的属性名,column是SQL中对应的列名
-
result标签用来设置普通属性的映射,property是实体类中的属性名,column是SQL中对应的列名
-
最后select标签的resultMap属性的值就是外面自定义的resultMap标签的id值
<resultMap id="empResultMap" type="Emp"> <id property="eid" column="id"></id> <result property="empName" column="emp_name"></result> <result property="empAge" column="emp_age"></result> <result property="empTall" column="emp_tall"></result> </resultMap> <select id="getAllEmp" resultMap="empResultMap"> select * from emp </select>
-
7.处理多对一映射关系的方法(三种)
多对一关系只需要在一的实体类中设置多的集合,在多的实体类中设置一的属性。
【例如:学生与班级,在学生类中添加班级对象,在班级类中添加学生集合】
-
方式一:使用resultMap级联赋值
-
方式二:在resultMap中使用association标签设置
-
方式三:分步查询,多个SQL查询出最终结果(以后使用最多的方式)
也是使用association标签,设置property(属性名)、select(第二条select语句的全路径.id)、column(子查询的条件)属性
例如:先查询出员工的部门id,然后根据部门id查询部门信息
【员工查询的xml中使用resultMap,设置相关字段,然后里面使用association设置下一个sql的信息(property【实体类中的对象属性】、select【下一个sql的全路径.id】、column【子查询的条件】)】
<resultMap id="empResultMap" type="Emp">
<id property="eid" column="id"></id>
<result property="empName" column="emp_name"></result>
<result property="empAge" column="emp_age"></result>
<association property="dept" select="com.junqing.mapper.getDept" column="deptId">
</resultMap>
<select id="getAllEmp" resultMap="empResultMap">
select * from emp where emp_name= #{name}
</select>
8.分步查询开启懒加载的注意事项
-
延迟加载会根据访问的数据确定是否加载下一个sql信息
例如:访问员工信息,就不会去加载查询部门的sql,访问部门信息,就会加载查询部门的子sql
-
association中的fetchType属性可以在开启延迟加载之后手动控制延迟加载的效果
lazy标识延迟加载,eager表示立即加载
9.处理一对多映射关系的方法(两种)
-
使用resultMap和collection(使用方法和association基本一样)
在resultMap标签中使用使用collection标签,设置property(属性名)、ofType(集合中存的实体类)属性,然后在里面设置集合中寸的实体类的映射关系
-
分步查询【和使用association的方法一样,只不过换成了collection】
10.动态SQL
10.1 if标签
if标签,可以根据条件判断是否将里面的语句拼接到需要执行的SQL中,只有test属性,用来写判断条件【如果test中有多个条件,直接使用and/or拼接】,判断完之后使用or或者and拼接sql
说明:if标签用在where字段后面,所以建议where后面加一个条件1=1,防止第一个if标签里面的条件不成里【可以使用where标签解决这个问题】
<select id="query" resultType="com.sky.entity.ShoppingCart">
select * from shopping_cart
<if test="dishId!=null and dishId!=''">
user_id=#{userId}
</if>
<if test="dishId!=null and dishId!=''">
and dish_id=#{dishId}
</if>
<if test="setmealId!=null and setmealId!=''">
and setmeal_id=#{setmealId}
</if>
<if test="dishFlavor!=null and dishFlavor!=''">
and dish_flavor=#{dishFlavor}
</if>
</select>
10.2 where标签
where标签会自动生成where关键字,并且将内容前多余的and 或 or 去掉【只能去掉内容前的多余and和or,不能去掉内容后的and和or】,所以可以把if标签放在where标签里面,如果where标签里面没有任何内容,那么where关键字不会起作用
<select id="query" resultType="com.sky.entity.Employee">
select * from employee
<where>
<if test="name != null and name != ''">
and name like concat('%',#{name},'%')
</if>
</where>
</select>
10.3 foreach标签
foreach标签用来实现批量删除和批量添加功能【将集合或者数组中的数据遍历出来,放到sql中】
foreach的属性:
collection:就是传进来的数组名
item:表示数组或集合中的每一个元素,如果是一个对象,那么可以用对象.属性(随便起个名字,只要#{}里面用他就行)
separator:表示分隔符
open:表示foreach以什么开始
close==属性:表示foreach以什么结束
接口方法:int deleteUser(@Param("usernames")Integer[] usernames);
//因为是数组所以会放到Map中以argX访问,使用Param自定义访问方式(不然下面的collection属性会访问不到数据)
<delete id="deleteUser">
delete from t_user where username in
<foreach collection="usernames" item="username" separator="," open="(" close=")">
#{username}
</foreach>
</delete>
接口方法:void insert(List<OrderDetail> orderDetails);
<insert id="insert">
insert into order_detail (name,image,order_id,dish_id,setmeal_id,dish_flavor,number,amount) values
<foreach collection="orderDetails" item="orderDetail" separator=",">
(#{orderDetail.name},#{orderDetail.image},#{orderDetail.orderId},#{orderDetail.dishId},#{orderDetail.setmealId},#{orderDetail.dishFlavor},#{orderDetail.number},#{orderDetail.amount})
</foreach>
</insert>
11.MyBatis的缓存
11.1 MyBatis的一级缓存
- 一级缓存默认开启,默认是sqlSession级别的(只对当前会话有效),缓存只针对于查询有效。同一个SqlSession查询的数据会被缓存,下次查询相同的数据就会直接从缓存中获取,不再访问数据库
- 一级缓存失效的四种情况:
- 使用不同的sqlSession
- 使用同一个sqlSession但是查询条件不同
- 使用同一个sqlSession,但是两次查询中间执行了任意一次增删改操作
- 使用同一个sqlSession,但是两次查询中间手动清空了缓存(sqlsession的clearCache方法)
11.2 MyBatis的二级缓存
- 二级缓存是SqlSessionFactory级别的,二级缓存需要手动开启
- 开启二级缓存的方法看上面的详细笔记
- 二级缓存必须在SQLSession关闭或提交之后有效(SqlSession的commit或者close方法),同时查询结果所转化的实体类必须可以序列化(实现了Serializable接口)
- 增删改可以使一级、二级缓存同时刷新
11.3 MyBatis的缓存查询问题
-
先查询二级缓存(因为范围大),如果没有再查询一级缓存,如果一级缓存还没有则查询数据库。
-
SqlSession关闭之后,一级缓存中的数据会缓存到二级缓存中
12.分页插件PageHelper的使用
- 引入依赖,就可以直接在Service中使用
- Service的方法中设置分页参数:PageHelper.startPage需要两个参数分别是当前查询页码,页面大小
- 然后调用Mapper方法(要求Mapper方法返回的对象是Page类型的对象)
- 最后从Page对象中过去需要的信息(如:总记录数、当前页数据等)