mybatis 环境 jdk1.8Mysql8.0Maven3.6idea SSM框架:配置文件 官方文档:https://mybatis.org/mybatis-3/zh/getting-start
环境
官方文档:https://mybatis.org/mybatis-3/zh/getting-started.html
MyBatis 是一款优秀的持久层框架
它支持自定义 sql、存储过程以及高级映射。
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了[Google code](Https://baike.baidu.com/item/google code/2346604?fromModule=lemma_inlink),并且改名为MyBatis。2013年11月迁移到Github。
从哪使用mybatis
maven仓库
<dependency> <groupId>org.mybatisgroupId> <artifactId>mybatisartifactId> <version>3.5.2version>dependency>
中文文档:https://mybatis.org/mybatis-3/zh/index.html
数据持久化
为什么需要持久化?
定义:
搭建环境----->导入MyBatis—>编写代码—>测试
搭建数据库
CREATE DATABASE `mybatis`;USE `mybatis`;CREATE DATABASE `mybatis`;CREATE TABLE `user`(`id` INT(20) NOT NULL,`name` VARCHAR(30) DEFAULT NULL,`pwd` VARCHAR(30) DEFAULT NULL,PRIMARY KEY(`id`))ENGINE=INNODB DEFAULT CHARSET=utf8;INSERT INTO `user`(`id`,`name`,`pwd`) VALUES(1,'狂神','123456'),(2,'张三','abcdef'),(3,'李四','987654');
新建项目
新建一个普通的maven项目
删除src目录
导入maven依赖
<dependencies> <dependency> <groupId>mysqlgroupId> <artifactId>mysql-connector-javaartifactId> <version>8.0.28version> dependency> <dependency> <groupId>junitgroupId> <artifactId>junitartifactId> <version>4.11version> <scope>testscope> dependency> <dependency> <groupId>org.mybatisgroupId> <artifactId>mybatisartifactId> <version>3.5.2version> dependency> dependencies>
创建mybatis的核心配置文件
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="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="passWord" value="root"/> dataSource> environment> environments>configuration>
编写mybatis工具类
// SqlSessionFactory--> SqlSessionpublic class MybatisUntils { private static SqlSessionFactory sqlSessionFactory; static { try { //获取 SqlSessionFactory对象 String resource = "org/mybatis/example/mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。 // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。 public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); }}
实体类
public class User { private int id; private String name; private String pwd; public User() { } 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 getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public User(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; }}
Dao接口
public interface UserDao { List<User> getUserList();}
接口实现类 由原来的Impl实现类转换为一个Mapper配置文件
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.hwt.dao.UserDao"> <select id="getUserList" resultType="com.hwt.pojo.User"> select * from mybatis.user select>mapper>
常见报错:
org.apache.ibatis.binding.BindingException: Type interface com.hwt.dao.UserDao is not known to the MapperReGIStry.
解决方法:
mybatis配置文件添加 <mappers> <mapper resource="com/hwt/dao/UserMapper.xml"/> mappers>
java.lang.ExceptionInInitializerErrorat com.hwt.dao.UserDaoTest.test(UserDaoTest.java:15)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
解决方法:
pom文件添加: <build> <resources> <resource> <directory>src/main/javadirectory> <includes> <include>***.propertiesinclude> includes> <filtering>falsefiltering> resource> <resource> <directory>src/main/resourcesdirectory> <includes> <include>***.propertiesinclude> includes> <filtering>falsefiltering> resource> resources> build>
MapperRegistry是什么?
核心配置文件中注册Mappers
junit测试
public class UserDaoTest { @Test public void test(){ //第一步,获取SqlSession对象 SqlSession sqlSession = MybatisUntils.getSqlSession(); // 执行sql //方式一:getMapper UserDao userDao = sqlSession.getMapper(UserDao.class); List<User> userList = userDao.getUserList(); for (User user : userList) { System.out.println(user); } //关闭sqlSession sqlSession.close(); }}
排错思路:
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 WEB 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) { // 你的应用逻辑代码}
namespace中的包名需要和Dao/mapper接口的包名一致!
选择、查询语句
编写接口
//根据ID查询用户 User getUserById(int id);
编写mapper文件中的sql语句
<select id="getUserById" resultType="com.hwt.pojo.User" parameterType="int"> select * from mybatis.user where id = #{id}select>
编写测试类
@Test public void getUserById(){ SqlSession sqlSession = MybatisUntils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); sqlSession.close(); }
<insert id="addUser" parameterType="com.hwt.pojo.User"> insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd});insert>
<update id="updateUser" parameterType="com.hwt.pojo.User"> update mybatis.user set name=#{name} ,pwd=#{pwd} where id=#{id};update>
<delete id="deleterUser" parameterType="int"> delete from mybatis.user where id=#{id};delete>
如果实体类,或者数据库中的表,字段或者参数过多,使用Map
//插入用户int addUser2(Map<String,Object> map);
<insert id="addUser2" parameterType="map"> insert into mybatis.user (id,name,pwd) values (#{userid},#{username},#{password});insert>
public void addUser2(){ SqlSession sqlSession = MybatisUntils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Object> map = new HashMap<>(); map.put("userid",5); map.put("username","hello"); map.put("password","2222222"); mapper.addUser2(map); sqlSession.commit(); sqlSession.close();}
mybatis中的#{value}和${value}的区别:
==重要:==接受从用户输出的内容并提供给语句中不变的字符串,这样做是不安全的。这会导致潜在的SQL注入攻击,因此你不应该允许用户输入这些字段,或者通常自行转义并检查。
mybatis-config.xml(官方推荐命名)
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
配置多套环境
mybatis’默认事务管理器(transactionManager):JDBC
默认数据源dataSource:POOLED
通过properties文件实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。(db.properties)
driver=com.mysql.cj.jdbc.Driverurl=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8username=rootpassword=root
<properties resource="db.properties"> <property name="username" value="root"/> <property name="pwd" value="root"/> properties>
类型别名是Java中的短名字
主要作用就是减少冗余,方便使用
<typeAliases> <typeAlias type="com.hwt.pojo.User" alias="user"/> typeAliases>
也可以只指定一个包名,mybatis会在包名下搜索需要的Java bean
扫描实体类的包,就是这个类的类名,但是首字母小写
<typeAliases> <typeAlias type="com.hwt.pojo.User"/> typeAliases>
当实体类较少的时候使用第一种,实体类多的时候使用第二种
第一种可以自定义命名,第二种需要借助实体类增加注解,而且需要mapper.xml文件有的所有实体类都需要改名
@Alias("user1")public class User { private int id; private String name; private String pwd; public User() { }}
常见的 Java 类型内建的类型别名。它们都是不区分大小写的
别名 | 映射的类型 |
---|---|
_byte | byte |
_char (since 3.5.10) | char |
_character (since 3.5.10) | char |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J(3.5.9 起废弃) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
设置名 | 描述 | 有效值 | 默认值 |
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true | false | false |
定义 SQL 映射语句,相当于直接告诉 MyBatis 到哪里去找映射文件
方式一:
<mappers> <mapper resource="com/hwt/dao/UserMapper.xml"/> mappers>
方式二:
<mappers> <mapper class="com.hwt.dao.UserMapper"/>mappers>
方式三:
<mappers> <package name="com.hwt.dao"/>mappers>
方式二和三需要注意:
接口和Mapper配置文件必须同名
接口和Mapper配置文件必须在同一个包下
可能会导致严重的并发问题
执行流程:
- 作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
SqlSessionFactory:
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。
有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式,保证全局只有一份变量。
说白了SqlSessionFactory可以看做一个连接池。
SqlSession:
每一个Mapper都是为了一个具体的业务
测试实体类字段不一致的情况
属性名:
@Alias("user1")public class User { private int id; private String name; private String password; public User() { }}
字段:
出现的问题:
User{id=1, name='老六', password='null'}
select * from mybatis.user where id = #{id}#类型转换器select id,name,pwd from mybatis.user where id = #{id}
解决方法:
select id,name,pwd as password from mybatis.user where id = #{id}
结果集映射
idnamepwdidnamepassword
<resultMap id="UserMap" type="user"> <result column="id" property="id"/> <result column="name" property="name"/> <result column="pwd" property="password"/> resultMap> <select id="getUserById" resultMap="UserMap" > select id,name,pwd from mybatis.user where id = #{id} select>
resultMap
元素是 MyBatis 中最重要最强大的元素resultMap
会怎样,这也是解决列名不匹配的另外一种方式。设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J(3.5.9 起废弃) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
在Mybaits中具体使用那个日志实现,在设置中设定
STDOUT_LOGGING 标准日志输出
在mybatis-config.xml中配置STDOUT_LOGGING 日志:
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/>settings>
什么是LOG4J?
导入lo4g包
<dependency> <groupId>log4jgroupId> <artifactId>log4jartifactId> <version>1.2.17version>dependency>
log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码log4j.rootLogger=DEBUG,console,file#控制台输出的相关设置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=[%c]-%m%n# 文件输出的相关设置log4j.appender.file = org.apache.log4j.RollingFileAppenderlog4j.appender.file.File=./log/kuang.loglog4j.appender.file.MaxFileSize=10mblog4j.appender.file.Threshold=DEBUGlog4j.appender.file.layout=org.apache.log4j.PatternLayoutlog4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n# 日志输出级别log4j.logger.org.mybatis=DEBUGlog4j.logger.java.sql=DEBUGlog4j.logger.java.sql.Statement=DEBUGlog4j.logger.java.sql.ResultSet=DEBUGlog4j.logger.java.sql.PreparedStatement=DEBUG
配置lo4g日志的实现
<settings> <setting name="logImpl" value="LOG4J"/> settings>
lo4g测试
使用:
在要使用Log4j的类中,导入包import org.apache.log4j.Logger;
日志对象,加载参数为当前类的class
static Logger logger = Logger.getLogger(UserDaoTest.class);
日志级别
logger.info("info:进入了testLo4g");logger.debug("DEBUG:进入了testLo4g");logger.error("error:进入了testLo4g");
分页的作用:
-- SQL语法:select * from user limit startIndex,paheSizeselect * from user limit 2,2;#第二个开始的两个
使用MyBatis实现分页,核心SQL
接口
//分页查询xxxxxxxxxx2 1分页查询2List getUserByLimit(Map map);
接口实现文件Mapper.xml
<select id="getUserByLimit" resultType="user" parameterType="map"> select * from user limit #{startIndex},#{pageSize}select>
测试
public void getUserByLimit(){ SqlSession sqlSession = MybatisUntils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Integer> map = new HashMap<>(); map.put("startIndex",1); map.put("pageSize",2); List<User> list = mapper.getUserByLimit(map); for (User user : list) { System.out.println(user.toString()); } sqlSession.close();}
输出的日志显示密码为空[com.hwt.dao.UserMapper.getUserByLimit]-==> Preparing: select * from user limit ?,? [com.hwt.dao.UserMapper.getUserByLimit]-==> Parameters: 1(Integer), 2(Integer)[com.hwt.dao.UserMapper.getUserByLimit]-<== Total: 2User{id=2, name='李四', password='null'}User{id=3, name='张三', password='null'}
解决方案:使用resultMap设置UserMap为返回值类型
<resultMap id="UserMap" type="user"> <result column="id" property="id"/> <result column="name" property="name"/> <result column="pwd" property="password"/> resultMap> <select id="getUserByLimit" resultMap="UserMap" parameterType="map"> select * from user limit #{startIndex},#{pageSize} select>
使用java自带的RowBounds类实现分页
sql不需要使用分页
接口
//使用RowBounds实现分页List<User> getRowBounds();
mapper.xml
<select id="getRowBounds" resultMap="UserMap"> select * from user;select>
测试
public void getRowBounds(){ //RowBounds实现 RowBounds rowBounds = new RowBounds(0, 2); SqlSession sqlSession = MybatisUntils.getSqlSession(); List<User> userList = sqlSession.selectList("com.hwt.dao.UserMapper.getRowBounds",null,rowBounds); for (User user : userList) { System.out.println(user); } sqlSession.close();}
关于接口的理解
从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
接口的本身反映了系统设计人员对系统的抽象理解。
接口应有两类:
第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);
一个体有可能有多个抽象面。抽象体与抽象面是有区别的。
三个面向区别:
接口
public interface UserMapper { @Select("select * from user") List<User> getUsers();}
注册 mapper-config.xml文件中注册
<mappers> <mapper class="com.hwt.dao.UserMapper"/>mappers>
测试
public class UserMapperTest { @Test public void getUsers(){ SqlSession sqlSession = MybatisUntils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUsers(); for (User user : userList) { System.out.println(user); } sqlSession.close(); }}
实现的本质是反射机制
**动态代理: **Java动态代理设计模式 - 大数据技术派 - 博客园 (cnblogs.com)
mybatis执行过程
在工具类创建的时候实现自动提交事务
public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(true);}
绑定接口注册
<mappers> <mapper class="com.hwt.dao.UserMapper"/>mappers>
编写接口,增加注解
public interface UserMapper { @Select("select * from user") List<User> getUsers(); //方法存在多个参数,所有的参数前面必须加上@Param("id")注解,sql中用的是注解中的id @Select("select * from user where id=#{id}") User getUsersById(@Param("id") int id); @Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})") int addUser(User user); @Update("update user set name=#{name},pwd=#{password} where id=#{id}") int uptateUser(User user); @Delete("delete from user where id=#{id}") int deleteUser(@Param("id") int id);}
测试类
public class UserMapperTest { @Test public void getUsers(){ SqlSession sqlSession = MybatisUntils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUsers(); for (User user : userList) { System.out.println(user); } sqlSession.close(); } @Test public void getUsersById(){ SqlSession sqlSession = MybatisUntils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUsersById(2); System.out.println(user); sqlSession.close(); } @Test public void addUser(){ SqlSession sqlSession = MybatisUntils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int i = mapper.addUser(new User(6, "高启强", "66666")); sqlSession.close(); } @Test public void uptateUser(){ SqlSession sqlSession = MybatisUntils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int i = mapper.uptateUser(new User(5, "高启盛", "111111")); sqlSession.close(); } @Test public void deleteUser(){ SqlSession sqlSession = MybatisUntils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int i = mapper.deleteUser(4); sqlSession.close(); }}
Lombok项目是一个java库,它可以自动插入到编辑器和构建工具中,增强java的性能。不需要再写getter、setter或equals方法,只要有一个注解,你的类就有一个功能齐全的构建器、自动记录变量等等。
Lombok注解:
@Getter and @Setter@FieldNameConstants@ToString@EqualsAndHashCode@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog@Data@Builder@SuperBuilder@Singular@Delegate@Value@Accessors@Wither@With@SneakyThrows
==@Data:==无参构造,get,set,tostring,hashcode,equals
使用步骤:
在IDEA中安装Lombok插件!
高版本idea安装Lombok:https://blog.csdn.net/weixin_44917577/article/details/118937025
在项目中导入Lombok的包
<dependency> <groupId>org.projectlombokgroupId> <artifactId>lombokartifactId> <version>1.18.26version> dependency>
实体类添加注解
@Data@AllArgsConstructor//有参构造@NoArgsConstructor//无参构造public class User { private int id; private String name; private String password;}
多对一:
需要用到的数据库
CREATE TABLE `teacher` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=INNODB DEFAULT CHARSET=utf8;INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师'); CREATE TABLE `student` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, `tid` INT(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `fktid` (`tid`), CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)) ENGINE=INNODB DEFAULT CHARSET=utf8;INSERT INTO `student` (`id`, `name`, `tid`) VALUES (1, '小明', 1); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (2, '小红', 1); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (3, '小张', 1); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (4, '小李', 1); INSERT INTO `student` (`id`, `name`, `tid`) VALUES (5, '小王', 1);
在pom.xml文件中导入Lombok
<dependencies> <dependency> <groupId>org.projectlombokgroupId> <artifactId>lombokartifactId> <version>1.18.26version> dependency>dependencies>
在pojo包下新建实体类eacher,Student
@Datapublic class Teacher { private int id; private String name;}
@Datapublic class Student { private int id; private String name; //学生需要关联一个老师 private Teacher teacher;}
建立Mappe接口
public interface StudentMapper {}
public interface TeacherMapper { @Select("select *from teacher where id=#{tid}") Teacher getTeacher(@Param("tid") int id);}
在resource目录新建与java目录相同的dao包目录并且建立Mapper.xml文件
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.hwt.dao.TeacherMapper">mapper>
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.hwt.dao.StudentMapper">mapper>
在核心配置文件中绑定注册我们的Mapper接口或者文件
<mappers> <mapper class="com.hwt.dao.StudentMapper"/> <mapper class="com.hwt.dao.TeacherMapper"/>mappers>
测试
public class MybatisTest { public static void main(String[] args) { SqlSession sqlSession = MybatisUntils.getSqlSession(); TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class); Teacher teacher = mapper.getTeacher(1); System.out.println(teacher); sqlSession.close(); }}
select s.id,s.name,t.name from student s,teacher t where s.tid=t.id
<select id="getStudent" resultMap="StudentTeacher"> select * from student; select> <resultMap id="StudentTeacher" type="student"> <result property="id" column="id"/> <result property="name" column="name"/> <association property="teacher" column="tid" javaType="student" select="getTeacher"/> resultMap> <select id="getTeacher" resultType="teacher"> select * from teacher where id = #{tid} select>
resultType后面的类型报错就先给该类起别名
<select id="getStudent2" resultMap="StudentTeacher2"> select s.id sid,s.name sname,t.name tname from student s,teacher t where s.tid=t.id select> <resultMap id="StudentTeacher2" type="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="Teacher"> <result property="name" column="tname"/> association> resultMap>
环境搭建:
实体类:
@Alias("teacher")@Datapublic class Teacher { private int id; private String name; //一个老师拥有多个学生 private List<Student> students;}
@Alias("student")@Datapublic class Student { private int id; private String name; //学生需要关联一个老师 private int tid;}
<select id="getTeacher" resultMap="TeacherStudent"> select s.id sid,s.name sname,t.name tname,t.id tid from mybatis.student s,mybatis.teacher t where s.tid=t.id and t.id=#{tid}select><resultMap id="TeacherStudent" type="Teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> <collection property="students" ofType="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <result property="tid" column="tid"/> collection>resultMap>
<select id="getTeacher2" resultMap="TeacherStudent2"> select * from teacher where id = #{tid} select> <resultMap id="TeacherStudent2" type="teacher"> <collection property="students" javaType="ArrayList" ofType="student" select="TeacherStudentByTeacherId" column="id"/> resultMap> <select id="TeacherStudentByTeacherId" resultType="student"> select * from student where tid = #{tid} select>
关联-association 【多对一】
集合-collection 【一对多】
javaType & ofType
javaType用来指定实体类中属性的类型
ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
注意点:
面试重点
什么是动态SQL:动态SQL就是根据不同的条件生成不同的SQL语句
动态 SQL 元素和 jsTL或任何基于类 XML 语言的文本处理器相似。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。 - if - choose (when, otherwise) - trim (where, set) - foreach
搭建环境
CREATE TABLE `blog`(`id` VARCHAR(50) NOT NULL COMMENT '博客id',`title` VARCHAR(100) NOT NULL COMMENT '博客标题',`author` VARCHAR(30) NOT NULL COMMENT '博客作者',`create_time` DATETIME NOT NULL COMMENT '创建时间',`views` INT(30) NOT NULL COMMENT '浏览量')ENGINE=INNODB DEFAULT CHARSET=utf8;
创建一个基础工程
导包
编写配置文件
编写实体类
@Alias("blog")@Datapublic class Blog { private int id; private String title; private String author; private Date createTime; private int views;}
编写实体类对应的Mapper接口和Mapper.xml文件并且插入实验数据
public interface BlogMapper { //插入数据 int addBlog(Blog blog);}
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.hwt.dao.BlogMapper"> <insert id="addBlog"> insert into blog (id, title, author, create_time, views) VALUE (#{id},#{title},#{author},#{createTime},#{views}); insert>mapper>
插入实验数据:
public class ITest { @Test public void addBlog(){ SqlSession sqlSession = MybatisUntils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); Blog blog = new Blog(); blog.setId(IdUtils.getId()); blog.setTitle("保证SQL的可读性,尽量保证通俗易懂"); blog.setAuthor("高启强"); blog.setCreateTime(new Date()); blog.setViews(6666); mapper.addBlog(blog); blog.setId(IdUtils.getId()); blog.setTitle("注意一对多和多对一中,属性名和字段的问题"); blog.setAuthor("高启盛"); mapper.addBlog(blog); blog.setId(IdUtils.getId()); blog.setTitle("如果问题不好排查错误,可以使用日志,建议使用log4j"); blog.setAuthor("麻子"); mapper.addBlog(blog); sqlSession.close(); }}
接口
//查询博客List<Blog> queryBlogIf(Map map);
XML
<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>
测试
@Testpublic void queryBlogIf(){ SqlSession sqlSession = MybatisUntils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); map.put("title","保证SQL的可读性,尽量保证通俗易懂"); map.put("author","高启强"); List<Blog> blogs = mapper.queryBlogIf(map); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close();}
**choose:**不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
<select id="queryBloGChoose" resultType="Blog" parameterType="map"> select * from blog <where> <choose> <when test="title != null"> title = #{title} when> <otherwise> and views = #{views} otherwise> choose> where> select>
**where:**where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除
<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <if test="title != null"> title = #{title} if> <if test="author != null"> and author = #{author} if> where>select>
**set:**set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)或者,可以通过使用trim元素来达到同样的效果:
<update id="updateBlog" parameterType="map"> update blog <set> <if test="title != null"> title = #{title}, if> <if test="author != null"> author = #{author} if> set> where id = #{id}update>
**trim:**prefix/suffix属性:如果trim后内容不为空,则增加某某字符串(作前缀/后缀);
如果trim后内容不为空,则删掉(前缀/后缀的)某某字符串。
<trim prefix="SET" suffixOverrides=","> ...trim>
我们覆盖了后缀值设置,并且自定义了前缀值
所谓动态SQL,本质还是SQL语句,只是我们可以在SQL层面去执行一个逻辑代码
就是将sql中一些功能片段提取出来,方便使用
使用sql标签抽取公共部分
<sql id="if-title-author"> <if test="title != null"> title = #{title} if> <if test="author != null"> and author = #{author} if>sql>
在需要使用的地方使用Include标签引用
<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <include refid="if-title-author">include> where>select>
注意:
//查询第1,2,3号的记录List<Blog> queryBlogForeach(Map map);
<select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P <where> <foreach item="item" index="index" collection="list" open="ID in (" separator="," close=")" nullable="true"> #{item} foreach> where>select>
foreach 元素的功能非常强大,它允许指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。也允许指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符。
**提示 :**可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
<select id="queryBlogForeach" resultType="Blog" parameterType="map"> select * from blog <where> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id =#{id} foreach> where>select>
小结:
动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了
建议:
什么是缓存【Cache】:
存在内存中的临时数据!
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用了从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题
为什么使用缓存?
什么样的数据能使用缓存?
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存也叫本地缓存:
与数据库同一次会话期间查询到的数据会放在本地缓存中。
以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
测试步骤
开启日志
测试在一个Session中查询两次相同的记录
查看日志输出
缓存失效的情况:
@Testpublic void queryUserById(){ SqlSession sqlSession = MybatisUntils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); //mapper.updateUser(new User(2,"aaaa","bbbbb")); sqlSession.clearCache(); System.out.println("*************************"); User user1 = mapper.queryUserById(1); System.out.println(user1); System.out.println(user==user1); sqlSession.close();}
一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间(相当于一个用户不断查询相同的数据,比如不断刷新)
一级缓存就是一个map!
步骤:
开启全局缓存
<setting name="cacheEnabled" value="true"/>
mapper文件添加二级缓存
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/><cache/>
测试
实体类没有序列化报错
Caused by: java.io.NotSerializableException: com.hwt.pojo.User
解决方式
@Data@AllArgsConstructor@NoArgsConstructorpublic class User implements Serializable { private int id ; private String name; private String pwd;}
小结
只要开启了二级缓存,在同一个Mapper下就有效
所有的数据都会先放在一级缓存中
只有当会话提交,或者关闭的时候才会提交到二级缓存中
缓存顺序:
注:一二级缓存都没有,查询数据库,查询后将数据放入一级缓存
介绍:
要在程序中使用ehcache,先要导包
<dependency> <groupId>org.mybatis.cachesgroupId> <artifactId>mybatis-ehcacheartifactId> <version>1.1.0version>dependency>
在mapper中指定使用我们的ehcache缓存实现
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <diskStore path="java.io.tmpdir/Tmp_EhCache"/> <defaultCache eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="259200" memoryStoreEvictionPolicy="LRU"/> <cache name="cloud_user" eternal="false" maxElementsInMemory="5000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LRU"/>ehcache>
以后开发会用Redis数据库来做缓存
来源地址:https://blog.csdn.net/qq_44624801/article/details/129019887
--结束END--
本文标题: 狂神MyBatis教程完整版(看这一篇就够了)入门到精通
本文链接: https://www.lsjlt.com/news/431309.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-06-05
2024-06-04
2024-06-04
2024-06-03
2024-06-03
2024-06-03
2024-06-04
2024-06-03
2024-06-03
2024-06-03
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0