MyBatis
概述
1.框架
framework,对通用代码的封装,是提前写好的一堆接口和类。
SSM:Spring + SpringMVC + MyBatis
SpringBoot
SpringCloud
MyBatis就是封装了JDBC
2. 三层框架
Web前端 -Ajax- 表现层 业务层 持久层 数据库
3.JDBC不足
sql语句写死在java程序中,改sql就要改java代码,违背开闭原则OCP; 给?传值繁琐; 将结果集封装到java对象也繁琐。
4.了解ORM思想
MyBatis是对于dao持久层的框架
轻量级
支持动态sql
sql与代码分离
MyBatis是一个半自动ORM(object relation mapping)映射的框架。对象关系映射。
O -> JVM中的Java对象
R -> 关系型数据库
M -> 映射【一个对象<->表中一条记录】
半自动:sql要程序员自己写
入门
1. 建一个空项目
1. New Project 2. Empty Project 3. Project Structure -> Project选择JDK版本 4. Settings -> Maven选择版本以及配置文件和仓库
注意:
如果不显示项目名
关闭idea
删除本地目录中该项目对应的.idea文件
重新打开idea即可
2. 第一个maven项目配置
点住空项目名。右键new Module...
选择Maven模板
右下角:Enable -Auto
<!-- 1.在pom文件中设置打包方式 --> <packaging>jar</packaging> <!-- 2.引入相关依赖 --> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <!-- 引入logback依赖,它实现了slf4j规范 --> <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.11</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <scope>provided</scope> </dependency> </dependencies>
3. 编写xml核心配置文件,用于配置数据库等 mybatis-config.xml 从官网拷 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <!-- 指定XxxMapper.xml文件的路径,从resource根路径开始找 --> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers> </configuration>
还有一个XxxMapper.xml文件,专门用来编写sql语句 一般一个表对应一个Mapper.xml文件 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace是咖啡豆的根路径 --> <mapper namespace="org.mybatis.example.BlogMapper"> <select id="selectBlog" resultType="Blog"> select * from Blog where id = #{id} </select> </mapper>
3. 目录解释
1.environments ==>表示环境的配置 可以配置多个环境
2.environment id="mysql" 指定是mysql的环境
3.transactionManager 配置事务管理器
它的type有两种
JDBC:表示使用jdbc 的方式来进行事务的管理,底层实例化JdbcTransaction对象
MANAGED:没有做任何操作 依赖于容器 以容器的方式的来进行事务管理,底层实例化ManagedTransaction对象
spring +mybatis ==设置参数MANAGED 表示已容器spring的方式来进行事务的管理
4.dataSource ==>数据源
POOLED ==> 表示一连接池的方式来管理连接 不会反复的创建与销毁连接 ,适用于开发测试环境。
UNPOOLED > 不使用连接池的方式来进行管理连接 使用的时候都创建连接与销毁连接 ,适用于小规模。
JNDI>必须在web环境,依赖于web容器 在tomcat 中可以进行配置(重点),生产环境优先使用这种。
4. 工具类
4.1 SqlSessionFactoryUtil
public enum SqlSessionFactoryUtil { // 单例 SSF; private SqlSessionFactory sqlSessionFactory; SqlSessionFactoryUtil() { try { // 从resources根路径开始的目录 InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); } catch (IOException e) { e.printStackTrace(); } } /** * 定义一个公有的方法返回sqlSessionFactory */ public SqlSessionFactory getSqlSessionFactory() { return sqlSessionFactory; } }
4.2 CommentMapper
public class CommentMapper { public SqlSession sqlSession; @Before public void before() { /* autoCommit是true,就表示没有开启事务,建议空着,开始事务 */ sqlSession = SqlSessionFactoryUtil.SSF.getSqlSessionFactory().openSession(true); } @After public void after() { //关闭资源 sqlSession.close(); } }
5. 日志logback
- 在pom文件中添加依赖
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.11</version> <scope>test</scope> </dependency>
- 引入logback必须的xml配置文件
// 文件名必须是logback.xml或者logback-test.xml,不能是其他名字 // 必须放在类的根路径下,不能是其他位置
<?xml version="1.0" encoding="UTF-8"?> <!-- 配置文件修改时重新加载,默认true --> <configuration debug="false"> <!-- 控制台输出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder" charset="UTF-8"> <!-- 格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 --> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <!-- mybatis log configure--> <logger name="com.apache.ibatis" level="TRACE"/> <logger name="java.sql.Connection" level="DEBUG"/> <logger name="java.sql.Statement" level="DEBUG"/> <logger name="java.sql.PreparedStatement" level="DEBUG"/> <!-- 日志输出级别,LOGBACK日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR--> <root level="DEBUG"> <appender-ref ref="STDOUT"/> <appender-ref ref="FILE"/> </root> </configuration>
6.老杜版本SqlSessionUtil
public class SqlSessionUtil { private SqlSessionUtil() { } private static SqlSessionFactory sqlSessionFactory; static { try { sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); } catch (IOException e) { e.printStackTrace(); } } /** * 全局的,服务器级别的,一个服务器当中定义一个即可 */ private static ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); public static SqlSession openSession() { SqlSession sqlSession = local.get(); if (sqlSession == null) { sqlSession = sqlSessionFactory.openSession(); // 将sqlSession绑定到当前线程上,只绑定一次 local.set(sqlSession); } return sqlSession; } public static void close(SqlSession sqlSession){ if (sqlSession != null) { sqlSession.close(); // 注意移除SqlSession对象和当前线程的绑定关系 // 因为Tomcat服务器支持线程池 local.remove(); } } }
增删改查
{} 相当于JDBC里的问号?
XxxMapper.xml里面的crud语句,注释掉的话,要用<!-- -->
{abc}是去entity类里面找对应的getAbc方法
insert delete update select
namespace是为了防止id冲突,完整的应该写成
sqlSession.方法名(namespace.id)
MyBatis规定namespace必须是dao接口的全限定类名,
if必须是dao接口中的方法名。
dao以后改叫mapper
select标签的resultType是pojo/entity的全限定名
resource都是从根路径开始
增删改查方法有两个参数:
一个是sql语句的id,从XxxMapper.xml中复制过来,另一个是对象,该对象封装了sql语句中的所有占位符参数#{}
XxxDaoImpl中的sqlSession调用的方法,传入参数,如果只有一个参数,那么在XxxMapper.xml中的sql语句里的#{}里可以随便写。 但建议见名知意。 如果是多参数,必须跟实体类Dao对应上。
WEB-INF目录下的web.xml文件 <web-app>标签中的metadata-complete=“false”的时候才是支持注解的, 如果是true,那么serlvet上面是不支持注解的。
批量删除
拼接表名${}
deleted from xx where id in(${})
模糊查询
xxx like “%”#{xxx}“%”
insert
useGeneratedKeys=''true''使用自动生成的主属性
keyProperty="id"指定主键值赋值给对象的哪个属性
insert into xxx values(null,xx,xxx)
就是给表的主属性id自动生成
Java生成类
是一个开源的分析、编辑和创建Java字节码的类库。通过使用Javassist对字节码操作作为JBoss实现动态“AOP”框架。
就不用写DaoImpl类
Javassist使用
引入依赖
<!-- https://mvnrepository.com/artifact/org.javassist/javassist --> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.28.0-GA</version> </dependency>
类型别名(typeAliases)
在config文件中,<typeAliases>标签, 里面的type写的是com.xxx.pojo.User alias写的是自己指定的别名,不区分大小写
注意:
Mybatis核心配置文件是有顺序的!!!
给所有类起别名,使用简明作为别名,不区分大小写。 <package name="com.xxx.pojo">
namespace不能起别名
参数处理
XxxMapper方法里面的参数
Param注解
多参数用这个
eg. User selectUser(@Param("name") String name,@Param("age")int age);
返回Map
返回的数据没有合适的实体类对应,可采用Map集合接收,字段名是key,字段值做value。
返回一个Map<String, Object> selectById(Long id);
返回多个List<Map<String, Object>> selectAll();
在XxxMapper接口上加一个@MapKey("id")
ResultMap封装结果映射
两个结果集id要一致 <resultMap id="结果集id" type="Xxx"> <result property="属性名" column="数据库表名"/> <result property="属性名" column="数据库表名"/> </resultMap> <select id="xxx" resultMap="结果集id"> </select>
开启驼峰命名自动映射
属性名必须遵循Java命名规范
<settings> <setting name="xxxx" value="true"/> </settings>
返回总记录条数
count(1)或count(*)都可以
count(字段名)的话会去除null
动态SQL
if标签
<select id=""> select * from table where 1=1 /* if标签中的test属性是必须的 test值是false或true 如果true则sql语句会拼接,反之不拼接 @Param,有pojo就写属性名 and表示并且,不能使用&& */ <if test="uName != null and uName !=''"> and age >= 18; </if> <if test="里面是Boolean类型"> and </if> </select>
where标签
<select id=""> select * from table /* where标签可以智能的去掉前面的and,但我们写的时候,and都写上*/ <where> <if test="uName != null and uName !=''"> and age >= 18; </if> <if test=""> and </if> </where> </select>
trim标签
<!-- trim prefix:加前缀 suffix:加后缀 prefixOverrides:删除前缀 suffixOverrides:删除后缀 --> <select id=""> select * from table <trim prefixOverrides="" suffix="" prefix="" suffixOverrides=""></trim> /*prefix="where" 在trim标签所有内容前加where suffixOverrides="and|or"把trim标签中内容的后缀and或or去掉 */ <trim prefix="where" suffixOverrides="and|or"> <if test="uName != null and uName !=''"> hobby like "%"#{hobby}"%" and </if> </trim> </select>
set标签
<!-- set标签 拼接set,可以智能的去掉句尾的, --> <update id=""> update table_name <set> <if test=""> ....., </if> </set> where id =#{id} </update>
choose when otherwise标签
<!-- choose when otherwise --> <!-- 一般三个标签一起用,保证至少会有一个执行 --> <!-- 等同于 if , else if ,else if, else--> <!-- when相当于else if ,otherwise相当于最后的else--> <select id=""> select * from table_name <where> <choose> <when test="true"> // 前面不用加and </when> <otherwise> </otherwise> </choose> </where> </select>
foreach标签
<!-- foreach collection:指定数组或集合 item:代表数组或集合中的元素 separator:循环之间的分隔符 open:foreach循环拼接的所有sql语句的最前面以什么开始,eg.( close:foreach循环拼接的所有sql语句最后面以什么结束,eg.) 这个括号是整个语句前面的,只有一个,循环之外 --> <select id="deletedByIds"> delete from t where id in( <foreach collection="" item="" separator=","> #{aaaa} </foreach> ) </select> <!--批量插入--> <insert id=""> insert into table_name values <foreach collection="" item="" separator=","> (null,#{},#{},#{}) </foreach> </insert> <!-- 批量删除 --> <delete id=""> delete from table_name where <foreach collection="" item="" separator="or"> id=#{} </foreach> </delete>
sql和include标签
<!--sql标签,include标签--> <sql id="起一个id名"> //重复出现的代码放这里 </sql> <select id="" resultMap=""> // include到这里就可以引用了 <include refid="起一个id名"></include> </select>
高级映射,延迟加载
多表间的映射 -- 高级映射
eg.学生表Student班级表Clazz
多个学生对一个班级
如何分主表和副表?
谁在前谁就是主表
多在前多就是主表,一在前依旧是主表
多对一
多是主表
<!--多对一,方式1:一条SQL,级联属性映射--> <resultMap id="studentResultMap" type="student"> <!--column里面查的值赋给property--> <id property="sid" column="sid"></id> <result property="sname" column="sid"/> <!--clazz是student类里的班级属性--> <result property="clazz.cid" column="cid"/> <result property="clazz.cname" column="cname"/> </resultMap> <select id="selectById" resultMap="studentResultMap"> select s.sid, s.sname, c.cid, c.cname from /*左外连接就是左表为主表,on后面是条件*/ t_stu s left join t_clz c on s.cid = c.cid where s.sid = #{sid} </select>
<!--多对一,方式二:一条SQL,association--> <resultMap id="studentResultMapAssociation" type="Studnet"> <id property="sid" column="sid"/> <result property="sname" column="sname"/> <!--一个Student关联一个Clazz,javaType用来指定需要映射的java类型--> <association property="clazz" javaType="Clazz"> <id property="cid" column="cid"/> <result property="cname" column="cname"/> </association> </resultMap> <select id="selectByIdAssociation" resultMap="studentResultMapAssociation"> select s.sid, s.sname, c.cid, c.cname from t_stu s left join t_clz c on s.cid = c.cid where s.sid = #{sid} </select>
<!--多对一,方式三:两条SQL分布查询,优点:可复用,支持懒加载--> <!--延迟加载(懒加载),用的时候载执行查询,不用的时候不查询 实际开发中,一般都会设置全局懒加载,在<setting name="lazyLoadingEnabled" value="true"> --> <!--两条SQL需要写两个Mapper.xml--> <resultMap id="studentResultMapByStep" type=""> <id property="sid" column="sid"/> <result property="sname" column="sname"/> <association property="clazz" select="" column="" fetchType="eager"/> <!--fetchType="eager"关闭改查询的懒加载--> </resultMap> <select id="" resultMap="studentResultMapByStep"> </select>
一对多
一是主表
<resultMap id="" type=""> <id property="cid" column=""/> <result property="cname" column="cname"/> <!-- 一对多 这里用collection ofType属性用来指定集合当中的元素类型 --> <collection property="stus" ofType="Student"> <id property="sid" column="sid"/> <result property="sname" column="sname"/> </collection> </resultMap> <select id="" resultMap=""> </select>
<!--一对多,方式二--> <resultMap id="" type=""> <id property="cid" column="cid"></id> <result property="cname" column="cname"> </result> <collection property="stus" select="sql语句id,就是对应接口上的方法,右键copy reference" column="cid"/> <!--这里的cid,跟下面select 【cid】 from xxx 要对应--> </resultMap> <select id="" resultMap=""> select cid,cname from t_clz where cid=#{cid} </select>
多对多
分解成两个一对多
缓存Cache
一级缓存:将查询的数据存储到SqlSession中
二级缓存:将查询的数据存储到SqlSessionFactory中
集成第三方缓存:eg.EhCache、MemCache等
缓存只针对DQL语句,也就是说缓存机制只对应select语句
只要执行一次增删改,缓存(包括一二级)就会清空
手动清空一级缓存的方法:sqlSession.clearCache();
默认情况下,MyBatis的二级缓存是开启的
<setting name ="cacheEnable" vlaue="true">全局性开启或关闭所有映射器配置文件中已配置的任何缓存,默认是true,无需设置
使用二级缓存要保证实力类是可序列化的,也就是要实现Serializable接口。
SqlSession对象关闭或提交之后,一级缓存中的数据才会被写入到二级缓存中,此时二级缓存才可用。
在需要使用二级缓存的SqlMapper.xml中添加配置:
<cache/>
集成EhCache
集成第三方缓存是为了代替mybatis自带的二级缓存,一级缓存是无法替代的。
逆向
就是根据数据库表逆向生成Java的POJO类,SqlMapper.xml文件,以及Mapper接口等。
需要借助逆向工程插件。
基础环境搭建
在pom.xml中加入插件依赖
<!--定制构建过程--> <build> <!--可配置多个插件--> <plugins> <!--其中的一个插件:mybatis逆向工程插件--> <plugin> <!--插件的GAV坐标--> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.4.1</version> <!--允许覆盖--> <configuration> <overwrite>true</overwrite> </configuration> <!--插件的依赖--> <dependencies> <!--mysql驱动依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> </dependencies> </plugin> </plugins> </build>
在gfneratorConfig.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:生成的是基础版,只有基本的增删改查。 MyBatis3:生成的是增强版,除了基本的增删改查之外还有复杂的增删改查。 --> <context id="DB2Tables" targetRuntime="MyBatis3"> <!--防止生成重复代码--> <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/> <commentGenerator> <!--是否去掉生成日期--> <property name="suppressDate" value="true"/> <!--是否去除注释--> <property name="suppressAllComments" value="true"/> </commentGenerator> <!--连接数据库信息--> <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/powernode" userId="root" password="root"> </jdbcConnection> <!-- 生成pojo包名和位置 --> <javaModelGenerator targetPackage="com.powernode.mybatis.pojo" targetProject="src/main/java"> <!--是否开启子包--> <property name="enableSubPackages" value="true"/> <!--是否去除字段名的前后空白--> <property name="trimStrings" value="true"/> </javaModelGenerator> <!-- 生成SQL映射文件的包名和位置 --> <sqlMapGenerator targetPackage="com.powernode.mybatis.mapper" targetProject="src/main/resources"> <!--是否开启子包--> <property name="enableSubPackages" value="true"/> </sqlMapGenerator> <!-- 生成Mapper接口的包名和位置 --> <javaClientGenerator type="xmlMapper" targetPackage="com.powernode.mybatis.mapper" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> </javaClientGenerator> <!-- 表名和对应的实体类名--> <table tableName="t_car" domainObjectName="Car"/> </context> </generatorConfiguration>
怎么用?
点开右边的maven -> plugins -> mybstis-generator -> mybatis-genrator:generate
双击即可
查一个selectByPrimaryKey
查所有selectByExample
分页PageHelper
limit分页
select * from t_car limit 2; select * from t_car limit 0,2; 这两个是等效的 第一个数字是开始的下标,第二个是需要显示的记录条数 第一条记录的下标是0
第pageNum页:
limit (pageNum-1)*pageSize, pageSize
PageHelper
第一步:引入依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.3.1</version> </dependency>
第二步:在mybatis-config.xml中配置分页拦截器
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin> </plugins>
第三步:写代码
一定要在DQL语句前,开启分页功能 int pageNum = 2; int pageSize = 3; // 开启分页 PageHelper.startPage(pageNum, pageSize); // 执行查询 mapper.selectAll(); // 获取分页信息,5是代号页码 new PageInfo<>(cars, 5);
注释
在接口类中写,XxxMapper 1.增 @insert(里面写sql) int insert(Car car); 2.删 @Delete(写sql语句) int deleteById(Long id); 3.改 @Update(写sql) int update(Car car) 4.查 @Select(sql语句) @Results({ @Result(property=“id”, column="id") @Result(property="carNum", column="car_num") @Result(property="brand", column="brand") }) Car selectById(Long id);
Druid连接池
配置pom.xml文件
<!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency>
创建DruidDataSourceFactory
MyDruidDataSourceFactory并继承PooledDataSourceFactory,并替换数据源。
public class MyDruidDataSourceFactory extends PooledDataSourceFactory { public MyDruidDataSourceFactory() { this.dataSource = new DruidDataSource();//替换数据源 } }
修改mybatis-config.xml
<dataSource type="com.qf.xxx.utils.DruidDataSourceFactory"><!--数据源工 厂--><property name="driverClass" value="${driver}"/> <property name="jdbcUrl" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>
注意:< property name="属性名" />属性名必须与com.alibaba.druid.pool.DruidAbstractDataSource
中一致。
本文作者:Ritchie里其
本文链接:https://www.cnblogs.com/wang-zeyu/p/16923896.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现