1.MyBatis简介
1.java的一个框架(framework),对jdbc不足进行的升级
ORM:对象 关系 映射
Object Relational Mapping
2.特点
mybatis 属于半自动化ORM框架,Hibernate属于全自动化ORM框架
-
mybatis 支持定制化SQL存储过程,基本映射以及高级映射
-
避免了几乎所有的JDBC代码中手动设置参数以及获取结果集
-
支持XML开发,也支持注解式开发【为了保证sql语句的灵活,所以mabatis大部分呢式采用XML开发】
-
将接口和java的POJOS(简单普通的java对象)映射成数据库中的记录
-
体积小易学,两个XML配置文件
-
完全做到了sql解耦合
-
提供了基本映射标签
-
提供了高级映射标签
-
提供了XML标签,支持动态sql的编写
2.mybatis入门
-
第一个mybatis程序
-
首先新建一个maven项目 在pom.xml中添加mybatis 和jdbc依赖,打包方式为jar
-
在main中的resources中新建文件mybatis-config.xml问价,这个文件内容,在mybaits文档中有具体内容
<?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="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/scidag"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> <!-- 加载xxxxMapper.xml文件--> <mapper resource="carMapper.xml"/> </mappers> </configuration>
-
在main中的resource中新建XxxxMapper.xml文件
内容如下
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="ggg"> <insert id="insert_car"> insert into t_car(id,car_num,brand,guided_price,produce_time,car_type) values (null ,"1102","长城坦克300",30.0,"2024-10-30","油车") </insert> </mapper>
这个文件是以后编写sql语句的,并且要添加到mybatis-config.xml中的《 <mappers>里 》
-
然后就是编写java程序了
public static void main(String[] args) throws Exception { //获取 SqlSessionFactoryBuilder SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); //获取 SqlSessionFactory (通过SqlSessionFactoryBuilder的build方法) //build 传参是inputstream 这里使用的mybatis 的工具类 Resources // ,使用的方法getResourceAsStream默认从根路径开始 //is1和is是一样的效果 InputStream is1=ClassLoader.getSystemClassLoader() .getResourceAsStream("mybatis-config.xml"); InputStream is= Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); //获取 SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); int insert_car = sqlSession.insert("insert_car");//这里传参是<insert > 的id System.out.println("添加了"+insert_car+"条"); //sqlsession 不会自动提交 所以需要我们手动提交 sqlSession.commit(); }
-
-
Mybatis事务管理机制
有两种一个是 jbdc
一个是managed
关于mybatis的事务管理机制。(深度剖析)
-
在mybatis-config.xml文件中,可以通过以下的配置进行mybatis的事务管理 <transactionManager type="JDBC"/>
-
type属性的值包括两个: JDBC(jdbc) MANAGED(managed) type后面的值,只有以上两个值可选,不区分大小写。
-
在mybatis中提供了两种事务管理机制: 第一种:JDBC事务管理器 第二种:MANAGED事务管理器
-
JDBC事务管理器: mybatis框架自己管理事务,自己采用原生的JDBC代码去管理事务: conn.setAutoCommit(false); 开启事务。 ....业务处理... conn.commit(); 手动提交事务 使用JDBC事务管理器的话,底层创建的事务管理器对象:JdbcTransaction对象。
如果你编写的代码是下面的代码: SqlSession sqlSession = sqlSessionFactory.openSession(true); 表示没有开启事务。因为这种方式压根不会执行:conn.setAutoCommit(false); 在JDBC事务中,没有执行conn.setAutoCommit(false);那么autoCommit就是true。 如果autoCommit是true,就表示没有开启事务。只要执行任意一条DML语句就提交一次。
-
MANAGED事务管理器: mybatis不再负责事务的管理了。事务管理交给其它容器来负责。例如:spring。 我不管事务了,你来负责吧。
对于我们当前的单纯的只有mybatis的情况下,如果配置为:MANAGED 那么事务这块是没人管的。没有人管理事务表示事务压根没有开启。
没有人管理事务就是没有事务。
-
JDBC中的事务: 如果你没有在JDBC代码中执行:conn.setAutoCommit(false);的话,默认的autoCommit是true。
-
重点: 以后注意了,只要你的autoCommit是true,就表示没有开启事务。 只有你的autoCommit是false的时候,就表示开启了事务。
-
-
实现crud
-
insert
//1.采用map集合 @Test public void InsertCatTestByMap(){ SqlSession sqlSession = MyBatisUtil.openSqlSeesion(); Map<String,Object> carmap=new HashMap<>(); carmap.put("carno","241"); carmap.put("brand","比亚迪"); carmap.put("guidePrice",10.0); carmap.put("productTime","2021-05-12"); carmap.put("type","电车"); sqlSession.insert("insert_car",carmap); sqlSession.commit(); sqlSession.close(); } /* 适用map集合在sql语句中就放map集合中的key <insert id="insert_car"> insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values (null , #{carno}, #{brand}, #{guidePrice}, #{productTime}, #{type}) </insert> */ //2.采用javabean public void InsertCarTestbyPojo(){ SqlSession sqlSession=MyBatisUtil.openSqlSeesion(); Car car=new Car(null,"424","宝马3系",45.01,"2023-11-10","新能源"); sqlSession.insert("insertnewcar",car); sqlSession.commit(); sqlSession.close(); } /* 采用javabean #{} 中就放属性名 ,在执行时,底层去找对应的get 方法 <insert id="insertnewcar"> insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values (null , #{carNum}, #{brand}, #{guidePrice}, #{productTime}, #{carType}) </insert> */
-
delete
//根据id去删 public void deleteById(){ SqlSession sqlSession=MyBatisUtil.openSqlSeesion(); sqlSession.delete("delbyid",4); sqlSession.commit(); sqlSession.close(); } /* <delete id="delbyid"> delete from t_car where id=#{id} </delete> */
-
update
//修改id 为10的一行 @Test public void updateByID(){ SqlSession sqlSession =MyBatisUtil.openSqlSeesion(); Car car =new Car(10L,"5553","大众polo",15.3,"2016-09-10","燃油车"); sqlSession.update("update",car); sqlSession.commit(); sqlSession.close(); } /** <update id="update"> update t_car set car_num=#{carNum} ,brand=#{brand}, guide_price=#{guidePrice}, produce_time=#{productTime}, car_type=#{carType} where id =#{id} </update> */
-
select
//根据id查 public void seleteByID(){ SqlSession sqlSession=MyBatisUtil.openSqlSeesion(); Object car = sqlSession.selectOne("selectbyid", 1); System.out.println(car); sqlSession.close(); } /* <select id="selectbyid" resultType="com.scidag.mybatis.pojo.Car"> select id, car_num as carNum,brand,guide_price as guidePrice, produce_time as productTime ,car_type as carType from t_car where id =#{id} </select> */
//查所有 public void selectAll(){ SqlSession sqlSession=MyBatisUtil.openSqlSeesion(); List<Car> cars = sqlSession.selectList("selectAll"); cars.forEach(car -> System.out.println(car)); sqlSession.close(); } /* <select id="selectAll" resultType="com.scidag.mybatis.pojo.Car"> select id, car_num as carNum,brand,guide_price as guidePrice, produce_time as productTime ,car_type as carType from t_car </select> */
-
-
在XxxxMapper.xml 中的 <mapper>标签的namespace 属性 用于指定sqlsession 中id 的文件位置
可以防止命名冲突
3.Mybatis配置文件
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> <settings> <setting name="logImpl" value="SLF4J"></setting> </settings> <environments default="development"> default 是默认访问id 为development环境的数据库 <!--在这个environments中 可以有多个配置 ,--> <environment id="development"> <transactionManager type="JDBC"/> 这个为事务管理器 只有两个值 分别是jdbc 和managed 在mybatis 提供了一个事务管理器接口 Transaction 在这个接口中有两个实现类jdbcTransaction ManagedTransaction 两个值对应两个实现类 JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。 MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为 <dataSource type="POOLED"> type 有三个属性值,UNPOOLED|POOLED|JNDI;分别对应不用数据库连接池,使用mybatis的数据库连接池,使用第三方的数据库连接池 dataSource 数据源,为程序提供Connection对象 <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/scidag"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <!-- 加载xxxxMapper.xml文件--> <mapper resource="carMapper.xml"/> </mappers> </configuration>
4.使用mybatis的代理机制取消xxxdao的实现类编写
发现xxxdao的实现类代码简单且无业务逻辑,mybatis提供了代理机制取消xxxdao的实现类编写,使用如下
private AccountDao ad=MyBatisUtil.openSqlSeesion().getMapper(AccountDao.class);
//采用mabatis的代理机制自动生成dao的实现类 //注意在xxxMapper.xml 中的namespace必须是xxxDao的全限定名称,sqlid 必须是xxxDao的方法 //后续再用ad.直接调用AccountDao 的方法,就是实现好的了
5.mybatis 小技巧
5.1#{}和${}区别(面试题)
-
#{}和${}都是占位符
-
#{} 底层是 PreparedStatement :先进行sql语句的编译,然后给sql语句的占位符?进行传值,避免了sql注入
-
${} 底层是 Statement :先进行sql语句的拼接,然后进行编译
-
一般使用#{},但是如果需要sql语句中的关键字放到sql语句中,只能用${},因为#{}是以值的形式放到sql语句中的,向sql语句中拼接表名也要用${},批量删除也要用${}
5.2给全限定类名起别名
-
在mybatis-config文件中使用<>typeAlias 属性type 别名alias
-
namespace 不能起别名
-
package<> name="包名" 将包下的所有文件起别名(这里是javabean的包名)
5.3 mappers
-
mapper <> 三个属性 resource ,配置文件放到类路径
-
url 绝对路径
-
class mapper接口的全限定接口名
-
package<> name 指定包 XXXmapper 和xml文件放到同级目录
5.4 使用自动生成的主键值
在XXXmapper 文件中编写sql语句时 添加属性
useGeneratedKeys="true" keyProperty="返回对象属性"
6.Mybatis参数处理
6.1简单类型参数
<select id="selectByid" resultType="student"> select * from t_student where id =#{id} </select>
public void TestSelectByid(){ SqlSession sqlSession = MyBatisUtil.openSqlSeesion(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> students = mapper.selectByid(1L); students.forEach(student -> System.out.println(students)); sqlSession.close(); }
6.2多参数处理
/* 在sql语句中,出现两个及以上的参数时 mabatis会自动创建map集合,并且map集合以这种方式存放的 map.put("arg0 或者 param0",参数1); map.put("arg1 或者 param1",参数2); */ <select id="selectByNameAndSex" resultType="student" > select * from t_student where name=#{name } and sex=#{sex} //这里的name,和sex是不能直接用的,要在接口中使用@Param("value")才可以在使用,如果没有@Param注解则使用arg0,arg1 使用注解起别名之后不能在用arg0,arg1,但是Param1可以用 </select>
使用大Map接受select返回值
@MapKey("id") Map<Long,Map<String,Object>> selectAllRetBigMap(); //mapper文件 <select id="selectAllRetBigMap" resultType="map"> select * from t_student </select>
@Test public void TestselectAllRetBigMap(){ SqlSession sqlSession = MyBatisUtil.openSqlSeesion(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); Map<Long, Map<String, Object>> map = mapper.selectAllRetBigMap(); System.out.println(map); sqlSession.close(); }
7.动态Sql
-
业务场景 批量删除,多条件查询
-
if标签
<select id="ifDynamicsSql" resultType="car" > select * from t_car where 1=1 <if test="brand != null and brand != ''"> and brand =#{brand} </if> <if test="guidePrice != null and guidePrice != '' "> and guide_price >#{guidePrice} </if> <if test="carType != null and carType != '' "> and car_type =#{carType} </if> </select> test属性是必须的
-
where标签
<where> <if test="brand != null and brand != ''"> and brand =#{brand} </if> <if test="guidePrice != null and guidePrice != '' "> and guide_price >#{guidePrice} </if> <if test="carType != null and carType != '' "> and car_type =#{carType} </if> </where>
-
trim标签
<trim prefix="WHERE" prefixOverrides="AND |OR "> ... </trim> 这个是等价与where标签的
-
set
<set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email},</if> <if test="bio != null">bio=#{bio}</if> </set> where id=#{id} trim与之等价的 <trim prefix="SET" suffixOverrides=","> ... </trim>
-
choose when otherwise
这三个一起使用 相当与 if ... else if ... else..
-
foreach
标签属性
-
collection 指定数组或集合
-
item 数组或集合中的元素
-
separator: 循环中的分隔符
-
open: foreach 循环中的拼接所有sql语句最前面以什么开始
-
close: foreach 循环中的拼接所有sql语句最后面以什么开始
-
-
批量插入和删除
<insert id="SqlInsetyByForeach" > insert into t_car values <foreach collection="carlist" item="car" separator=","> (null,#{car.carNum},#{car.brand},#{car.guidePrice},#{car.productTime},#{car.carType}) </foreach> </insert> <delete id="SqlDeleteByIdAlot"> delete from t_car where id in( <foreach collection="ids" item="car" separator=","> #{car} </foreach> ) </delete>
8.高级映射及延迟加载
8.1多对一
主表 是多表 一就是副表
ffumybatis查询多表有三种方式
\1. 使用结果集
<resultMap id="resultMapID" type="pojo类型"> <id property="表1id" column="sid" /> <result property="表1字段" column="sname" /> <result property="表2id" column="cid" /> <result property="表2字段名" column="cname" /> <!-- 这里副表(表2)的字段名和id 要使用 引用对象.字段名 的方式使用 --> </resultMap> <select id="来自XxxMapper的接口方法" resultMap="resultMapID"> select s.sid,s.sname,c.cid,c.cname from t_stu s left join t_clazz c on s.cid=c.cid where s.sid =#{sid} </select>
\2. 使用
association <resultMap id="ResultMapID" type="Student"> <id property="sid" column="sid" /> <result property="sname" column="sname"/> <association property="clazz" javaType="附表引用对象类型" > <id property="cid" column="cid"/> <result property="cname" column="cname"/> </association> </resultMap> <select id="来自xxxMapper 的接口方法" resultMap="ResultMapID"> select s.sid,s.sname,c.cid,c.cname from t_stu s left join t_clazz c on s.cid=c.cid where s.sid =#{sid} </select>
\3.分步查询 多条sql语句 优点 可复用 支持懒加载
<!--对主表的查询 --> <resultMap id="resultMapid" type="Student"> <id property="sid" column="sid"/> <result property="sname" column="sname"/> <association property="clazz" select="对下一张表 的Mapper映射文件 接口方法的全限定名称" column="cid" /> <!--这里的colume 的值要写附表的id 如果写clazz 会找不到副表的数据 --> </resultMap> <select id="接口方法名" resultMap="resultMapid"> select sid,sname,cid from t_stu where sid = #{sid} </select>
<!-- 这是副表的--> <select id="selectByidStep2" resultType="Clazz"> select cid,cname from t_clazz where cid =#{cid} </select>
8.2一对多
1.collection
<!-- 使用collection实现 一对多的 多表查询--> <resultMap id="ResultMapCollection" type="Clazz"> <id property="cid" column="cid" /> <result property="cname" column="cname" /> <collection property="stus" ofType="Student"> <id property="sid" column="sid" /> <result property="sname" column="sname" /> </collection> </resultMap> <select id="selectByCollection" resultMap="ResultMapCollection"> select c.cid,c.cname,s.sid,s.sname from t_clazz c left join t_stu s on s.cid=c.cid where c.cid=#{cid} </select> 2.分步查
<resultMap id="ResultMapStep1" type="Clazz"> <id property="cid" column="cid"/> <result property="cname" column="cname"/> <collection property="stus" select="com.scidag.mybatis.mapper.StudentMapper.selectByidStep2" column="cid" /> <!-- 这里有个疑问 就是使用 collection 和association 效果一样 具体有区别吗?--> </resultMap>
<select id="selectByStep1" resultMap="ResultMapStep1"> select cid,cname from t_clazz where cid =#{cid} </select>
<!-- 副表的查询 -->
<select id="selectByidStep2" resultType="Student"> select sid,sname from t_stu where cid =#{cid} </select>
9.Mybatis的缓存
缓存 cache
通过减少io 的方式 提高程序的执行效率
mybatis 缓存
包括
一级缓存 查询的数据放到sqlsession中
两次DQL之间做一下两件事都会是一级缓存清空 /失效
-
执行update ,delete ,insert
-
执行sqlSession.clearCache();
二级缓存 查询的数据放到sqlsessionFactory中
二级缓存失效
两次查询中出现增删改操作
10Mybatis逆向工程
动态生成pojo XxxMapper XXXMapper.xml
-
在根目录(src /main/java/resources)中编写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:生成的是基础版,只有基本的增删改查。 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/scidag" userId="root" password="root"> </jdbcConnection> <!-- 生成pojo包名和位置 --> <javaModelGenerator targetPackage="com.scidag.mybatis.pojo" targetProject="src/main/java"> <!--是否开启子包--> <property name="enableSubPackages" value="true"/> <!--是否去除字段名的前后空白--> <property name="trimStrings" value="true"/> </javaModelGenerator> <!-- 生成SQL映射文件的包名和位置 --> <sqlMapGenerator targetPackage="com.scidag.mybatis.mapper" targetProject="src/main/resources"> <!--是否开启子包--> <property name="enableSubPackages" value="true"/> </sqlMapGenerator> <!-- 生成Mapper接口的包名和位置 --> <javaClientGenerator type="xmlMapper" targetPackage="com.scidag.mybatis.mapper" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> </javaClientGenerator> <!-- 表名和对应的实体类名--> <table tableName="t_car" domainObjectName="Car"/> </context> </generatorConfiguration>
-
在pom.xml添加配置
<build> <!-- 构建过程中用到的插件 --> <plugins> <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 --> <plugin> <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.33</version> </dependency> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.4.1</version> </dependency> </dependencies> </plugin> </plugins> </build>
11.mybatis 使用PageHelper
-
首先在pom.xml引入依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/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>
注意 : 这个plugins 要写在typeAliases 标签后面
-
正常写Mapper接口方法和xxxMapper.xml映射文件
sql语句不用加limit
-
在执行sql语句前加
PageHelper.startPage(pageNum,pageSize);
-------------------
最后这里还有几个注解,动力节点讲的比较简单 就是本来在 XxxMapper.xml 中写的sql语句
通过 @Insert @ Delete @ Select @ Update @Retsult 写在XxxMapper 接口中
其实Mybatis 是不太建议使用这几个注解的 ,虽然这几个注解提高了代码简洁性 ,但是一旦sql语句比较长,可能会报错,而且sql语句很长,不利于后面修改