Mybatis学习笔记
mybatis框架 持久层
Spring
Spring MVC 表示层
shiro 安全框架
git 版本控制工具
项目阶段
Junit
使用Junit做单元测试的目的:
-
方便运行代码,人为查看结果符不符合预期
-
使用断言测试让机器去判断结果符不符合预期,a开头的很多junit断言测试
User one = UserDao.getOne(1); asserEquals(null,one);
@Test注释的方法成为测试用例
测试注解使用的要求:
- 要注解在某个方法上
- 该方法要求无参,返回值为空,且为public
在test里面添加参数
@Test(timeout=1000)
限时测试:timeout属性指定毫秒值,要求该单元测试用例在规定时间内完成
@Before
作用于某个方法上
作用:所有的测试单元执行之前执行该方法,每执行一个测试单元就会执行一次Before
要求:
- 要注解在某个方法上
- 该方法要求无参,返回值为空,且为public
@After
作用于某个方法上
作用:在所有单元测试用例之后执行该方法(多用于资源回收)
要求:
- 要注解在某个方法上
- 该方法要求无参,返回值为空,且为public
@BeforeClass
在整个测试类的所有测试用例运行之前运行一次
- 要注解在某个方法上
- 该方法要求无参,返回值为空,还要被public static修饰!!
@AfterClass
在整个测试类的所有测试用例运行完之后运行一次
- 要注解在某个方法上
- 该方法要求无参,返回值为空,还要被public static修饰!!
@Ignore
互虐这个测试用例,但是会在总计结果中显示统计信息
@Runwith()
套件测试,同事测试多个测试类。所谓Runner提供单元你测试运行的环境,默认为Junit4.class
@RunWith(Suite.class)
@Suite.SuiteClasses(UserDaoImplTest.class,AppTest.class)
@Suite.SuiteClasses指定该套件中包含了哪些测试类
1.简介
-
MyBatis 是一款优秀的持久层框架
-
它支持自定义 SQL、存储过程以及高级映射。
-
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
-
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
1.1.获取mybaties
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
- github
- 官方中文文档:https://mybatis.org/mybatis-3/zh/index.html
1.2.持久层
Dao层(数据库操作层),Service层(业务层),Control层(专门接收用户请求层)。
-
持久层:完成持久化工作的代码块
-
层是界限十分明显的
持久化是一个动作,持久层是一个概念。
1.3.为什么需要Mybatis
- 方便
- 传统的JDBC太复杂,简化,框架,自动化
- 帮助程序员将数据存入到数据库中
- 不用mybatis也可以,更容易上手
- 用的人特别多
spring,spring mvc,springboot
2.第一个mybatis程序
步骤:搭建环境-》导入mybatis-》编写代码-》测试
搭建数据库环境
2.1导入xml依赖
<dependencies>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<!-- 测试工具junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
2.2.创建一个maven子项目
-
配置mybatis的核心配置文件
<?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核心配置文件--> <configuration> <!-- environments配置多个环境--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <!-- 在xml中&符号需要进行转义&--> <property name="url" value="jdbc:mysql:///mybatis"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> </configuration>
2.3编写mybatis工具类
//第一步从 XML 中构建 SqlSessionFactory
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//第二步既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
## 2.4编写代码
- 实体类
```java
public class User {
private int id;
private String name;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User() {
}
public User(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
-
Dao接口
public interface UserDao { List<User> getUserList(); }
-
接口实现类
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace命名空间绑定一个Maper接口--><mapper namespace="com.liu.Dao.UserDao"><!-- id对应UserDao的方法,resultType对应实体类--> <select id="getUserList" resultType="com.liu.Pojo.User"> select * from mybatis.user </select></mapper>
2.5测试
注意:
org.apache.ibatis.binding.BindingException: Type interface com.liu.Dao.UserMaper is not known to the MapperRegistry.
MapperRegistry是什么?
核心配置文件中注册mapers
- junit测试
public class UserDaoTest { @Test public void test() { //第一步获取sqlSession对象 SqlSession sqlSession = MybatisUtil.getSqlSession(); //执行SQL //方式一getMapper UserMaper mapper = sqlSession.getMapper(UserMaper.class); List<User> userList = mapper.getUserList(); for (User user : userList) { System.out.println(user); } //关闭sqlSession sqlSession.close(); }}
可能遇到的问题:
1.配置文件没有注册
2.绑定接口错误
3.方法名不对
4.返回类型不对
5.Maven导出资源问题
3.CRUD
1.namespace
namespace中的报名要和Mapper接口的报名一致
2.select
选择,查询语句;
- id:就是对应namespace中的方法名
- resulType:sql语句执行的返回值!
- paramterType:参数类型!
1.编写接口
2.编写对应的mapper中的sql
3.测试
3.Insert
UserMapper:
//插入一条记录 public void insertOne(User user);
UserMapper.xml
<insert id="insertOne" parameterType="com.liu.Pojo.User"> insert into user values (#{id},#{name},#{password}) </insert>
Test:
//插入用户 @Test public void insertOne(){ SqlSession sqlSession = MybatisUtil.getsqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.insertOne(new User(4,"liulang","123456")); //提交事务 sqlSession.commit(); sqlSession.close(); }
4.Update
UserMapper:
//修改记录 public int alterUser(User user);
UserMapper.xml
<update id="alterUser" parameterType="com.liu.Pojo.User"> update user set name=#{name},password=#{password} where id=#{id}; </update>
Test:
//修改信息 @Test public void alterUser(){ SqlSession sqlSession = MybatisUtil.getsqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int i = mapper.alterUser(new User(4, "liulang", "123")); if (i>0) System.out.println("修改成功"); else System.out.println("修改失败"); //提交事务 sqlSession.commit(); sqlSession.close(); }
5.Delete
UserMapper:
//删除用户 public int deleteUser(int id);
UserMapper.xml
<delete id="deleteUser" parameterType="int"> delete from user where id=#{id} </delete>
Test:
//删除用户 @Test public void deleteUser(){ SqlSession sqlSession = MybatisUtil.getsqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int i = mapper.deleteUser(4); if (i>0) System.out.println("删除成功"); else System.out.println("删除失败"); //提交事务 sqlSession.commit(); sqlSession.close(); }
注意:增删改都需要提交事务才会生效!!! sqlSession.commit();
6.错误
-
namespace="com.liu.Mapper.UserMapper";必须写详细路径
-
sql语句标签错误
-
注册mapper;resource的路径要用/而不是点
<mappers> <mapper resource="com/liu/Mapper/UserMapper.xml"/></mappers>
4.maven资源导出问题
<build> <resources><!-- 设置正常情况的resources目录下的properties文件--> <resource><!-- 配置路径--> <directory>src/main/resources</directory> <includes><!-- 包含什么文件--> <include>**/*.properties</include> <include>**/*.xml</include> </includes> </resource><!-- 设置java路径的properties文件--> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> </resource> </resources> </build>
7.非常实用的Map
如果我们的实体类或则数据中的表,字段过多,我们考虑使用Map。
修改或添加部分信息:
UserMapper:
//使用map修改部分数据 public int alterUser2(Map<String,Object> map);
UserMapper.xml
<update id="alterUser2" parameterType="map"> update user set name=#{username} where id=#{userid}; </update>
Test:
//使用map更新部分信息 @Test public void alterUser2(){ SqlSession sqlSession = MybatisUtil.getsqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String, Object> map = new HashMap<String, Object>(); map.put("username","李小盛"); map.put("userid",3); int i = mapper.alterUser2(map); if (i>0) System.out.println("修改成功"); else System.out.println("修改失败"); //提交事务 sqlSession.commit(); sqlSession.close(); }
根据多个条件查询信息:
UserMapper:
//指定多条件查询 public User getUserById2(Map<String,Object> map);
UserMapper.xml
<select id="getUserById2" parameterType="map" resultType="com.liu.Pojo.User"> select * from user where id=#{id} and name=#{name} </select>
Test:
@Test public void getUserById2(){ SqlSession sqlSession = MybatisUtil.getsqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String, Object> map = new HashMap<String, Object>(); map.put("id",3); map.put("name","李小盛"); User user = mapper.getUserById2(map); System.out.println(user); sqlSession.close(); }
Map传递参数,直接在sql中去粗key即可。
使用Map的时候一定要注意map里面的键值对必须和UserMapper.xml中使用的变量对应!!!
对象传递参数,直接在sql中去对象的属性。
只有一个基本数据类型参数的情况下,可以直接在sql中使用
多个参数用map或注解!
8.模糊查询
1.java执行的时候传递通配符%
List<User> users = mapper.likeUser("%李%");
2.在sql拼接中使用通配符
<select id="likeUser" resultType="com.liu.Pojo.User"> select * from user where name like "%"#{name}"%"</select>
第二种写法:
<select id="queryAll" resultMap="USER" parameterType="map"> select * from user <where> <if test="name!=null"> name like '%${name}%' </if> </where></select>
4.XML配置
1.核心配置文件
mybaits-config,xml
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
2.环境配置
Mybatis可以配置成适应多环境
注:尽管可以配置多个环境,但是每个sqlSessionFactory实例只能选择一种环境
学会使用配置多套运行环境!
Mybatis默认的事务管理器就是JDBC,连接池:POOLED
3.属性(properties)
通过propertis来引入配置文件
1.编写一个配置文件:
driver=com.mysql.jdbc.Driverurl=jdbc:mysql:///mybatisusername=rootpassword=123456
2.在核心配置文件中引入:在核心配置文件中所有的标签都有规定的先后顺序
资源在核心配置同一个路径下可以使用相对路径定位
没有添加额外参数<properties resource="db.properties"/>添加额外参数<properties resource="db.properties"> <property name="username" value="root1"/> <property name="password" value="123456"/> </properties>
- 可以直接引入配置文件
- 可以在其中增加一些配置属性
- 如果两种方式有相同的属性,优先使用外部配置文件的属性!!!
4.类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写
第一种方式:
<typeAliases> <typeAlias type="com.liu.Pojo.User" alias="User"/> </typeAliases>
第二种方式
可以指定一个包名,Mybatis会在这个包名下面搜索需要的JavaBean,比如:
扫描的实体类的名称,它的默认别名就是这个类的首字母小写。
<typeAliases> <package name="com.liu.Pojo"/> </typeAliases>x
在实体类比较少的情况使用第一种,
如果实体类十分多的时候,建议第二种
第一种可以DIY别名,第二种不行。如果非要在实体类上增加注解,注解方式大于默认搜索
@Alias("hello")public class User(){};
5.映射器(mappers)
MapperRegistry:注册绑定我们需要的Mapper文件
方式一:【推荐使用】
<mappers> <mapper resource="com/liu/Mapper/UserMapper.xml"/> </mappers>
方式二:使用class方式绑定注册
<mappers> <mapper class="com.liu.Mapper.UserMapper"/> </mappers>
方式三:使用包扫描注册绑定
<mappers> <package name="com.liu.Mapper"/> </mappers>
方式二和方式三注意!!:
- 接口和它的Mapper配置文件必须同名
- 接口和它的Mapper配置文件必须在同一个包下面
6.生命周期
生命周期类和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
- 一旦创建了SqlSessionFactory就不在需要他了
- 局部变量
SqlSessionFactory:
- 可以想象为数据库连接池
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- 因此 SqlSessionFactory 的最佳作用域是应用作用域。(程序开始他就开始,程序结束他就结束)
- 简单的就是使用单例模式或者静态单例模式。
SqlSession:
- 连接到连接池的一个请求!
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
- 用完之后赶紧关闭,否则资源被占用
每一个Mapper代表一个具体的业务。
5.解决属性名和字段名不一致的问题
解决方法:
- 起别名
<select id="getUserById" parameterType="int" resultType="com.liu.Pojo.User"> select id,name,password as pwd from user where id = #{id} </select>
2.resultMap
结果集映射
id name passwordid name pwd
resultMap
元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets
数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
<!-- resultMap就是使用这里的id,类型映射为实体类 --> <resultMap id="UserMap" type="User"><!-- property表示实体类属性,column表示数据库字段映射。将字段映射为实体类的对应属性 --> <result property="id" column="id"/> <result property="name" column="name"/> <result property="pwd" column="password"/> </resultMap> <select id="getUserList" resultMap="UserMap"> select * from user </select>
6.日志
6.1日志工厂
如果一个数据库操作出现了异常,我们需要排错。日志就是我们最好的助手。
曾经:sout、debug
现在:日志工厂!
- SLF4J
- LOG4J【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING【掌握】
- NO_LOGGING
在Mybatis中具体使用哪一个日志实现,在设置中设定!
STDOUT_LOGGING:标准日志输出
配置设置的时候一定要注意格式、大小写、空格等问题
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
6.2Log4j
Apache的开源项目log4j是一个功能强大的日志组件,提供方便的日志记录。
-
日志可以输出到控制到或GUI组件
-
我们可以控制每一条日志输出的格式
-
通过设置每一条日志的级别,我们能够更细致的控制日志生成的过程。
-
通过配置文件来进行配置,不需要我们修改应用的代码
1.导入LOG4J的包
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version></dependency>
2.log4j.properties
### 配置根 ####配置日志类型log4j.rootLogger = debug,console ,file### 设置输出sql的级别,其中logger后面的内容全部为jar包中所包含的包名 ###log4j.logger.org.apache=debuglog4j.logger.java.sql.Connection=debuglog4j.logger.java.sql.Statement=debuglog4j.logger.java.sql.PreparedStatement=debuglog4j.logger.java.sql.ResultSet=debug### 配置输出到控制台 ###log4j.appender.console = org.apache.log4j.ConsoleAppenderlog4j.appender.console.Target = System.outlog4j.appender.console.Threshold=DEBUGlog4j.appender.console.layout = org.apache.log4j.PatternLayoutlog4j.appender.console.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{ 1 }:%L - %m%n### 配置输出到文件 ###log4j.appender.file = org.apache.log4j.RollingFileAppenderlog4j.appender.file.File = ./logs/mybatis.loglog4j.appender.file.Append = truelog4j.appender.file.Threshold = DEBUGlog4j.appender.file.layout = org.apache.log4j.PatternLayoutlog4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n### 配置输出到文件,并且每天都创建一个文件 ###log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppenderlog4j.appender.dailyRollingFile.File = logs/log.loglog4j.appender.dailyRollingFile.Append = truelog4j.appender.dailyRollingFile.Threshold = DEBUGlog4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayoutlog4j.appender.dailyRollingFile.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n#日志输出级别log4j.logger.org.mybatis=DEBUGlog4j.logger.java.sql=DEBUG#log4j.logger.java.sql.Statement=DEBUG#log4j.logger.java.sql.ResultSet=DEBUGlog4j.logger.java.sql.PraparedStatement=DEBUG
3.配置log4j为日志的实现
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
4.Log4j的使用,直接测试运行查询就行了
简单使用
1.在需要使用log4j的类中,导入包,包不要导错了,需要导入apeach的log4j
2.生成日志对象,加载参数为当前类的class
static Logger logger = Logger.getLogger(mybatistest.class);
3.日志级别
logger.info("info:进入testlog4j");logger.debug("debug:进入testlog4j");logger.error("error:进入testlog4j");
7.分页
为什么要分页?
- 减少数据的处理量
{参数名}:按照preparedstatement解析sql语句时所使用的的?占位符
${参数名}:传什么参数,就按字符串拼接方式进行填充
7.1使用limit分页,语法:
select * from user limit statIndex,pagesize;select * from user limit 2,5select * from user limit 3
使用mybatis实现分页,核心SQl
1.接口
//分页查询public List<User> limitUser(Map<String,Integer> map);
2.Mapper.xml
<!-- 实现分页查询--> <select id="limitUser" parameterType="map" resultType="user"> select * from user limit #{startIndex},#{pageSize} </select>
3.测试
//分页查询@Testpublic void limitUser(){ SqlSession sqlSession = MybatisUtil.getsqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Integer> map = new HashMap<String, Integer>(); map.put("startIndex",0); map.put("pageSize",3); List<User> users = mapper.limitUser(map); for (User user : users) { System.out.println(user); }}
7.2useGeneratedKeys
如果想要拿到插入数据后自动递增的主键值时,使用useGeneratedKeys=true
KeyColumn:指定自动递增的列明
KeyProperty:指定传入的参数对象中用于存放自动递增的值对应的属性
8.使用注解开发
8.1面向接口编程
-
接口定义与实现的分离
-
接口的本身反应了系统设计人员对系统的抽象理解
-
接口也应有两个类:
- 一个是对一个个体的抽象abstract
- 一个是对个体某个方面的抽象interface
8.2使用注解开发
1.注解直接在接口上实现
@Select("select * from user")List<User> getUsers();
2.需要在核心配置文件中绑定接口!
<mappers> <mapper class="com.liu.Mapper.UserMapper2"/></mappers>
3.测试
//注解方式@Testpublic void zjUsers(){ SqlSession sqlSession = MybatisUtil.getsqlSession(); UserMapper2 mapper = sqlSession.getMapper(UserMapper2.class); List<User> users = mapper.getUsers(); for (User user : users) { System.out.println(user); } sqlSession.close();}
本质:反射机制实现
底层:动态代理
8.3CRUD
我们可以在工具类创建的时候实现自动提交事务!
return sqlSessionFactory.openSession(true);
编写接口,增加注解
//方法存在多个参数时,所有的参数前面必须加上@Param("id")注解 @Select("select * from user where id=#{id} and name=#{name}") public User getUserById(@Param("id") int id,@Param("name") String username); @Insert("insert into user values (#{id},#{name},#{password})") public int addUser(User user); @Update("update user set name=#{name},password=#{password} where id=#{id}") int UpdateUser(User user); @Delete("delete from user where id=#{id}") public int delUser(@Param("id") int pid);
【我们必须将接口绑定到注册到我们的核心配置文件中】
<mappers> <package name="com.liu.Mapper"/> </mappers>
关于@Param(")注解
- 基本类型的参数或则String类型的参数需要加上
- 引用数据类型不需要加
- 如果只有一个数据类型的话可以忽略,建议也加上
- 我们在SQL中引用的就是我们这里的@Param设置的属性名
{}和${}区别:
-
{}和${}这两个语法是为了动态传递参数而存在的,是Mybatis实现动态SQL的基础
- #{} 是 占位符 :动态解析 -> 预编译 -> 执行
- ${} 是 拼接符 **:动态解析 -> 编译 -> 执行
类似statement和preparestatement
9.Lombok
- java library
- plugs
- build tools
使用步骤:
1.在IDEA安装plug,Lombok插件
2.在maven中导入Lombok依赖
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency>
@Data:,toString,GetandSet,hashcode,equals
@AllArgsConstructor@NoArgsConstructor
全参构造和无参构造
3.在实体类上加注解即可
10.多对一
- 对学生来说,关联,多个学生关联一个老师【多对一】
- 对老师而言,集合,一个老师辅导多个学生【一对多】
环境搭建:
1.导入Lombok
2.新建实体类Teacher,Student
3.建立Mapper接口
4.建立Mapper.xml文件
5.在核心配合文件中绑定Mapper接口或文件
6.测试
需求查询所有的学生信息以及对应的老师信息。
1.按照查询嵌套处理
<!-- 使用结果集映射需要使用resultMap--><select id="getStudent" resultMap="StudentTeacher"> select * from student</select><resultMap id="StudentTeacher" type="student"> <!-- 复杂的属性,我们需要单独处理--> <!-- 对象使用association--> <result property="id" column="id"/> <result property="name" column="name"/> <association property="teacher" column="id" javaType="Teacher" select="getTeacher"/> <!-- 集合使用collection--> <!-- <collection property=""--></resultMap><select id="getTeacher" resultType="teacher"> select * from teacher where id=#{id}</select>
第二条select语句中的where里面的条件#{},花括号里面随便写都不会影响查询结果
2.按照结果嵌套处理
<!-- 按照结果嵌套处理--><select id="getStudent2" resultMap="StudentTeacher2"> select s.id sid,s.name sname,t.name tname from student as s,teacher as t where s.id=t.id</select><resultMap id="StudentTeacher2" type="student"> <result column="sid" property="id"/> <result column="sname" property="name"/> <association property="teacher" javaType="Teacher"> <result property="tid" column="id"/> <result property="name" column="tname"/> </association></resultMap>
查询结果:
Student(id=1, name=liulang, cno=1, teacher=Teacher(id=1, name=chentao, cno=1))
Student(id=2, name=lixiaosheng, cno=1, teacher=Teacher(id=1, name=chentao, cno=1))
Student(id=3, name=zhenghaiqing, cno=1, teacher=Teacher(id=1, name=chentao, cno=1))
11.一对多
一个老师拥有多个学生
1.结果集映射
<select id="getTeacher2" resultMap="TeacherStudent"> select s.id sid,s.name sname,t.name tname,t.id tid,t.cno tcno,s.cno scno from student s,teacher t where s.cno=t.cno</select><resultMap id="TeacherStudent" type="teacher"> <result property="id" column="tid"/> <result property="cno" column="tcno"/> <result property="name" column="tname"/> <collection property="students" ofType="student"> <result property="id" column="sid"/> <result property="cno" column="scno"/> <result property="name" column="sname"/> </collection></resultMap>
2.子查询方式
<!-- 子查询--> <select id="getTeacher" resultMap="TeacherStudent1"> select * from teacher </select> <resultMap id="TeacherStudent1" type="Teacher"> <result property="id" column="id"/> <result property="name" column="name"/> <result property="cno" column="cno"/><!-- column用户传递给子查询作为where条件 --> <collection property="students" column="cno" javaType="ArrayList" ofType="Student" select="getStudent"/> </resultMap> <select id="getStudent" resultType="Student"> select * from student where cno=#{cno} </select>
查询结果:
Teacher(id=1, name=chentao, cno=1, students=[Student(id=1, name=liulang, cno=1), Student(id=2, name=lixiaosheng, cno=1), Student(id=3, name=zhenghaiqing, cno=1)])
总结:
1.关联-association【多对一】
2.集合-collection【一对多】
3.javaType & ofType
1.javaType用来指定实体类中属性的类型
2.ofTyp用来指定映射到list或则集合中的pojo类型,泛型中的集合类型
注意:
- 保证SQL的可读性,尽量保证通俗易懂
- 注意一对多和多对一中,属性名和字段的问题
- 如果问题不好排查,可以使用日志,建立使用log4j
12.动态SQL
什么是动态SQL:根据不同的条件生成不同的SQL语句
所谓动态SQL:本质还是SQL语句,只是我们可以再SQL层面,去执行一个逻辑代码
1.搭建环境
开启驼峰命名:只应用于数据库映射java使用的驼峰命名
<settings> <!-- 开启驼峰命名映射--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
2.where
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
使用where标签如果你满足的第一个条件在where 后面添加了一个and,会被这个标签自动删除and然后正常执行,如果你没有过滤条件,那么where标签会将where字句删除
<where> <if test="state != null"> state = #{state} </if> <if test="title != null"> AND title like #{title} </if> <if test="author != null and author.name != null"> AND author_name like #{author.name} </if> </where>
3.IF
BlogMapper.xml
<insert id="addBlog" parameterType="blog"> insert into blog values(#{id},#{title},#{author},#{createTime},#{view}); </insert> <select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog where 1=1 <if test="title != null"> and title=#{title} </if> <if test="author != null"> and author = #{author} </if> </select>
test:
public void queryBlogIf(){ SqlSession sqlSession = MybatisUtil.getsqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); map.put("title","Mybatis");// map.put("author","ll"); List<Blog> blogs = mapper.queryBlogIf(map); for (Blog item : blogs) { System.out.println(item); } sqlSession.close(); }
4.choose(swtich)
<select id="queryBlogChoose" resultType="blog" parameterType="map"> select * from blog <where> <choose> <when test="title != null"> title=#{title} </when> <when test="author != null"> author=#{author} </when> <otherwise> view > 0 </otherwise> </choose> </where> </select>
作用:和java的swtich功能类似
- 只会满足其中之一,如果存在满足多个条件的话也只会满足第一个条件
5.Set
作用:set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号```xml
<update id="updateBlogSet" parameterType="map" > update blog <set> <if test="title != null"> title=#{title}, </if> <if test="author != null"> author=#{author}, </if> <if test="view != null"> view=#{view}, </if> </set> where id = '35a166859fb84884b1e18732a05516ff'</update>
6.foreach(几乎不用)
我们想查出数据库前两条数据
<select id="queryfreeBlog" resultType="Blog"> select * from Blog <where> <foreach collection="list" item="id" open="and (" close=")" separator="or"> id = #{id} </foreach> </where> </select>
open:循环开始之前拼接的内容;
close结:循环结束拼接的内容
separator:指定循环中的分隔符
collection:循环遍历的内容
item:遍历的单个变量,在标签体重来使用
@Test public void queryfreeBlog(){ SqlSession sqlSession = MybatisUtil.getsqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); ArrayList<String> str = new ArrayList<String>(); str.add("35a166859fb84884b1e18732a05516ff"); str.add("6d5cb2ff745c4f658eefd4b21add44f9"); map.put("list",str); List<Blog> blogs = mapper.queryfreeBlog(map); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); }
结果:
Blog{id='35a166859fb84884b1e18732a05516ff', title='这不是结局', author='小新', createTime=2021-07-29, view=998}
Blog{id='6d5cb2ff745c4f658eefd4b21add44f9', title='java', author='刘星', createTime=2021-07-29, view=1005}
注意:
如果这里我们没有传递任何参数,就会自动删除where条件,将会查出所有信息
7.SQL片段
有的时候,我们可能会将一些功能的部分抽取出来,方便复用
<sql id="if-author-title"> <if test="title != null"> and title=#{title} </if> <if test="author != null"> and author = #{author} </if> </sql> <select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <include refid="if-author-title"/> </where> </select>
实现代码的复用
使用sql标签提取公共部分
在使用的地方用include标签引入SQL片段通过id引入
注意:
- 最好基于单表来定义SQL片段
- 不要存在where标签
- 最好只要一些IF判断就好了
13.缓存(了解)
1.什么是缓存?
- 存在内存中的临时数据
- 将用户数据查询到的数据放在缓存中,用户去查询数据就不用从磁盘上查询,从缓存中读取,从而提高效率,解决了高并发系统的性能问题
2.为什么要使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率
3.什么样的数据能使用缓存?
查询:连接数据库,耗内存
解决:一次查询的结果,给他暂存在一个可以直接读取的地方--》内存:缓存
我们再次查询相同数据的时候我们走缓存,就不用走数据库了。
14.Mybatis缓存
- Mybatis包含一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存,缓存可以极大地提升查询效率
- Mybatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
- 为了提高扩展性,mybatis定了缓存接口Cache,我们可以通过实现Cache接口来定义二级缓存
一级缓存
- 一级缓存也叫本地缓存
- 与数据库同义词会话期间查询到的数据会被放在本地缓存中
- 以后如果需要获取相同的数据的话,直接从缓存中拿,没有必要在去查数据库
实验步骤
1.开启日志
2.测试在一个Session中查询两次相同的记录
3.查看日志输出
@Test public void queryAllUser(){ SqlSession sqlSession = MybatisUtil.getsqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.queryAllUser(6); System.out.println(users); System.out.println("============================================="); List<User> users1 = mapper.queryAllUser(6); System.out.println(users1); System.out.println(users==users1); sqlSession.close(); }
增删改会刷新原来的东西
缓存失效的情况:
- 增删改都有可能改变原来的数据,必定会刷新缓存
- 查询不同的Mapper.xml
- 手动清理缓存
- 查询不同的东西
小结:一级缓存默认是开启的,只在一次SqlSessionFactory中有效,也就是拿到链接到关闭连接区间有效。一级缓存也是无法关闭的。
一级缓存作用域就是sqlsession对象,存储的内容,查询后的结果集,在sqlSession对象中有Map结果,Map的值就是结果集的对象。
二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
- 工作机制:
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果当前会话关闭了,这个会话对应的一级缓存就没有了;但是我们想要的是,会话关闭了,一级缓存中的数据会被保存到二级缓存中
- 新的会话查询信息,就可以从二级缓存中获取内容;
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
步骤:
1.开启缓存设置
<setting name="cacheEnabled" value="true"/>
2.要是用二级缓存需要在Mapper中开启
<!--在当前的xml中开启二级缓存--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
也可以自定义一些参数
3.测试
@Test public void queryAllUser(){ SqlSession sqlSession1 = MybatisUtil.getsqlSession(); SqlSession sqlSession2 = MybatisUtil.getsqlSession(); UserMapper mapper = sqlSession1.getMapper(UserMapper.class); List<User> users = mapper.queryAllUser(6); System.out.println(users); sqlSession1.close(); UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class); List<User> users2 = mapper2.queryAllUser(6); System.out.println(users2); sqlSession2.close(); }
只进行了一次查询
小结:二级缓存只能在一个namespace内有效,也就是只在一个xml文件内。所有的数据都会先放在一级缓存中,只有当会话提交或关闭的时候转存到二级缓存
问题!:我们需要将实体类序列化
implements Serializable
查找顺序
自定义缓存--ehcache
ehcache是一种广发使用的开源java分布式缓存