MyBatis介绍
- MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
MyBatis 本是 apache 的一个开源项目iBatis, 2010 年这个项目由 apache software foundation 迁移到了 google code,并且改名为 MyBatis 。2013 年 11 月迁移到 Github。
MyBatis完成数据访问层的优化.它专注于sql语句.简化了过去JDBC繁琐的访问机制.
怎么获取mybatis?
maven仓库
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency>
搜搜jar包的坐标网站:https://mvnrepository.com/
mybatis中文文档:https://mybatis.net.cn/
MyBatis快速入门
添加所需依赖
修改pom.xml文件,添加MyBatis需要的依赖
<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>5.1.32</version></dependency></dependencies>
指定资源文件
修改pom.xml文件,指定资源文件(保证目录中的所有资源文件都拷贝到target中)
<build><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include><include>**/*.properties</include></includes></resource><resource><directory>src/main/resources</directory><includes><include>**/*.xml</include><include>**/*.properties</include></includes></resource></resources></build>
添加配置文件
jdbc.properties文件
jdbc.driverClassName=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf8#jdbc.url=jdbc:mysql://localhost:3308/ssm?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=truejdbc.username=rootjdbc.password=root
添加核心配置文件(SqlMapConfig.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">
```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">
<environments default="development"><environment id="development"><!--配置事务管理器type:指定事务管理的方式JDBC:事务的控制交给程序员处理MANAGED:由容器(如:Spring)来管理事务注意:这里的type值必须大写--><transactionManager type="JDBC"></transactionManager><!--配置数据源type:指定不同的配置方式JNDI:java命名目录接口,在服务器端进行数据库连接池的管理(一般不使用)POOLED:使用数据库连接池UNPOOLED:不使用数据库连接池--><dataSource type="POOLED"><!--配置数据库连接的四个参数--><property name="driver" value="${jdbc.driverClassName}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments>
<mappers><mapper resource="StudentMapper.xml"></mapper></mappers>
- 添加StudentMapper.xml文件```java<?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">
<?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:是整个文件的大标签,用来开始和结束xml文件属性:namespace:指定命名空间(相当于包名),用来区分不同mapper.xml文件中相同的id属性--><mapper namespace="xs"><!--使用JDBC原始方式查询全部学生的方式:List<Student> selectAll();resultType:指定查询返回的结果集的类型,如果是集合,则必须是泛型的类型parameterType:如果有参数,则通过它来指定参数的类型(使用别名,查看别名映射表)--><select id="selectAll" resultType="com.ityg.pojo.Student">select id,name,email,agefrom student</select><!--按主键id查询学生信息Student getAllById(Integer id);--><select id="getAllById" resultType="com.ityg.pojo.Student" parameterType="int">select id,name ,email,agefrom studentwhere id = #{id}</select></mapper>
junit测试
student为基本的JavaBean实体类。
public class test {@Testpublic void testSelectAll(){InputStream in = null;SqlSessionFactory factory = null;SqlSession sqlSession = null;try {//1.使用文件流读取核心配置文件(SqlMapConfig.xml)in = Resources.getResourceAsStream("SqlMapConfig.xml");//2.创建SqlSessionFactory工厂factory = new SqlSessionFactoryBuilder().build(in);//3.获取sqlSession对象sqlSession = factory.openSession();//4.完成查询操作List<Object> list = sqlSession.selectList("selectAll");list.forEach(student -> System.out.println(student));} catch (IOException e) {e.printStackTrace();}finally {//5.关闭sqlSessionif (sqlSession!=null) {sqlSession.close();}}}}
为实体类注册别名
单个注册
<typeAliases><typeAlias type="com.ityg.pojo.Student" alias="student"></typeAlias></typeAliases>/*type:实体类的全限定名alias:别名*/
批量注册
<package name="com.ityg.pojo"></package>//name:实体类的包路径//别名为类名的驼峰命名法
设置日志输出
<!--设置日志输出底层执行的代码name和value就设置这两个值--><settings><setting name="logImpl" value="STDOUT_LOGGING"/></settings>
注意标签的书写位置(顺序) properties, settings, typeAliases, environments, mappers(依次从上到下)
使用动态代理
动态代理存在的意义:在三层架构中,业务逻辑层需要通过接口访问数据库访问层的功能。然而使用了MyBatis框架,无法在业务逻辑层访问xml文件中的功能。
动态代理可以实现。
动态代理的规范
- UsersMapper.xml文件与UsersMapper.java的接口必须同一个目录下.
- UsersMapper.xml文件与UsersMapper.java的接口的文件名必须一致,后缀不管.
- UserMapper.xml文件中标签的id值与与UserMapper.java的接口中方法的名称完全一致.
- UserMapper.xml文件中标签的parameterType属性值与与UserMapper.java的接口中方法的参数类型完全一致.
- UserMapper.xml文件中标签的resultType值与与UserMapper.java的接口中方法的返回值类型完全一致.
- UserMapper.xml文件中namespace属性必须是接口的完全限定名称com.ityg.mapper.UsersMapper
- 在SqlMapConfig.xml文件中注册mapper文件时,使用class=接口的完全限定名称com.ityg.mapper.UsersMapper.(一般使用package方式注册
)
使用动态代理访问CRUD操作步骤:
- 创建数据表(Users)
- 新建maven工程 ,修改目录,添加实体类
- 修改pom.xml文件,添加依赖
- 添加jdbc.propertis文件到resources目录下
- 添加SqlMapConfig.xml(MyBatis核心配置)文件
- 添加mapper文件夹,新建UsersMapper接口
- 在mapper文件夹下,新建UsersMapper.xml文件,完成增删改查功能
- 添加测试类,测试功能
代码实现:
这里从第5步开始
5-创建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><!--1.读取属性文件 jdbc.properties--><properties resource="jdbc.properties"></properties><!--2.设置控制台日志输出--><settings><setting name="logImpl" value="STDOUT_LOGGING"/></settings><!--3.给实体类注册别名--><typeAliases><!--为该目录下所有实体自动注册别名--><package name="com/ityg/pojo"/></typeAliases><!--4.配置数据库环境变量--><environments default="development"><environment id="development"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="${jdbc.driverClassName}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments><!--5.注册mapper.xml文件--><mappers><!--注意这里使用类的全限定名,打点的方式--><!--<mapper class="com.ityg.mapper.UsersMapper"></mapper>--><!--批量注册--><package name="com.ityg.mapper"/></mappers></configuration>
6-创建业务接口(UsersMapper接口)
public interface UsersMapper {//查询所有用户List<Users> getAll();//根据主键查询用户Users getById(int id);//根据用户名模糊查询List<Users> getByName(String name);//用户的更新int update(Users users);//增加用户int insert(Users users);//根据主键删除用户int delete(int id);}
7-创建执行业务操作的xml配置文件(UsersMapper.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="com.ityg.mapper.UsersMapper"><!-- //查询所有用户List<Users> getAll();--><select id="getAll" resultType="users">select id,username,birthday,sex,address from users</select><!--//根据主键查询用户Users getUserById(int id);--><select id="getUserById" parameterType="int" resultType="users">select id,username,birthday,sex,address from userswhere id=#{id}</select><!--//根据用户名模糊查询List<Users> getByName(String name);--><select id="getByName" parameterType="string" resultType="users">select id,username,birthday,sex,address from users where username like '%${name}%'</select><!-- //用户的更新int update(Users users);private Integer id;private String userName;private Date birthday;private String sex;private String address;--><update id="update" parameterType="users">update users set username=#{userName},birthday=#{birthday},sex=#{sex},address=#{address}where id=#{id}</update><!--//增加用户int insert(Users users);--><insert id="insert" parameterType="users">insert into users(username,birthday,sex,address)values(#{userName},#{birthday},#{sex},#{address})</insert><!--//根据主键删除用户int delete(int id);--><delete id="delete" parameterType="int" >delete from users where id=#{id}</delete></mapper>
8-测试
public class mapperTest {SqlSession sqlSession = null;SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");//取出动态代理的对象,完成接口中方法的调用,实则是调用xml文件中的相应标签的功能UsersMapper mapper = null;@Beforepublic void openSqlSession() throws IOException {InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);sqlSession = factory.openSession();mapper = sqlSession.getMapper(UsersMapper.class);}@Afterpublic void closeSqlSession(){sqlSession.close();}@Testpublic void testGetUserById(){//取出动态代理的对象,完成接口中方法的调用,实则是调用xml文件中的相应标签的功能UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);Users user = usersMapper.getById(8);System.out.println(user);}@Testpublic void testUpdate() throws ParseException {Users user = new Users(7,"haha",sdf.parse("2000-01-01"),"1","十堰");int count = mapper.update(user);System.out.println(count);//切记切记切记,增删改 必须要手工提交事务sqlSession.commit();}@Testpublic void testInsert() throws IOException, ParseException {mapper.insert(new Users("heeh",sdf.parse("2022-01-02"),"2","武汉"));sqlSession.commit();}@Testpublic void testDelete(){int i = mapper.delete(27);System.out.println(i);sqlSession.commit();}}
优化mapper.xml文件注册
<!--注册mapper.xml文件--><mappers><!--绝对路径注册--><mapper url="/////"></mapper><!--非动态代理方式下的注册UsersMapper.xml文件放在resource目录下--><mapper resource="UsersMapper.xml"></mapper><!--动态代理方式下的单个mapper.xml文件注册--><mapper class="com.ityg.mapper.UsersMapper"></mapper><!--批量注册--><package name="com.ityg.mapper"></package></mappers>
#{}:占位符,${}:字符串拼接或替换
传参时大部分使用#{}传参,它的底层使用的是PreparedStatement对象,是安全的数据库访问 ,防止sql注入.
{}里如何写,看parameterType参数的类型
1)如果parameterType的类型是简单类型(8种基本(封装)+String),则#{}里随便写 2)parameterType的类型是实体类的类型,则#{}里只能是类中成员变量的名称,而且区分大小写.
- ${}字符串拼接或字符串替换
字符串拼接,一般用于模糊查询中.建议少用,因为有sql注入的风险.
- 如果parameterType的类型是简单类型,则${}里随便写,但是分版本,如果是3.5.1及以下的版本,只以写value
- 如果parameterType的类型是实体类的类型,则${}里只能是类中成员变量的名称.(现在已经少用)
优化后的模糊查询(以后都要使用这种方式)
parameterType属性
- 如果是一个参数需要写
- 如果是两个参数,则不写
@Param()的使用
字符串替换
需求:模糊地址或用户名查询(根据传入的参数,动态的执行是按用户名还是地址进行模糊查询) select from users where username like ‘%小%’; select from users where address like ‘%市%’
//为参数添加注解List<Users> getByNameOrAddress(@Param("columnName") String columnName,@Param("columnValue") String columnValue);<!-- 此处使用的是@Param注解里的名称where ${columnName}:这里也还存在sql注入问题没有弄明白--><select id="getByNameOrAddress" parameterType="string" resultType="users">select id,username,birthday,sex,address from userswhere ${columnName} like concat('%',#{columnValue },'%')</select>
如果参数超过一个,则parameterType不写
返回当前插入数据的主键
需要在insert标签中加入下面代码
select last_insert_id();
标签的参数详解: keyProperty: 实体类对象(users)的哪个属性来接返回的主键值 resultType:返回的主键的类型 order:在插入语句执行前,还是执行后返回主键的值
<!--//增加用户int insert(Users users);--><insert id="insert" parameterType="users"><selectKey keyProperty="id" resultType="int" order="AFTER">select last_insert_id();</selectKey>insert into users(username,birthday,sex,address)values(#{userName},#{birthday},#{sex},#{address})</insert>
UUID(生成全球唯一随机字符串)
@Testpublic void testUuid(){UUID uuid = UUID.randomUUID();System.out.println(uuid.toString());}
动态sql
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
和标签
:当多种类型的查询语句的查询字段或者查询条件相同时,可以将其定义为常量,方便调用。 :引用 中定义的常量。 <!-- 定义代码片段--><sql id="allColumn">id,username,birthday,sex,address</sql><select id="getAll" resultType="users">select-- 引用定义的代码片段<include refid="allColumn"></include>from users</select>
和 标签 :进行条件判断,test条件判断的取值可以是实体类的成员变量,可以是map的key,可以是@Param注解的名称。 :进行多条件拼接,在查询,删除,更新中使用,一般开发复杂业务的查询条件时,如果有多个查询条件,通常会使用 标签来进行控制。标签可以自动的将第一个条件前面的逻辑运算符 (or ,and) 去掉。 <!--根据User给的属性,进行模糊查询List<Users> getByCondition(Users users);--><select id="getByCondition" parameterType="users" resultType="users">select <include refid="allColumn"></include> from users<where><if test="userName != null and userName != ''">and username like concat('%',#{userName},'%')</if><if test="birthday != null">and birthday = #{birthday}</if><if test="sex != null and sex != ''">and sex = #{sex}</if><if test="address != null and address != ''">and address like concat('%',#{address},'%')</if></where></select>
注意每一个if里面的语句前面需要加一个and
标签
使用set标签可以将动态的配置 SET 关键字,并剔除追加到条件末尾的任何不相关的逗号。使用 if+set 标签修改后,在进行表单更新的操作中,哪个字段中有值才去更新,如果某项为 null 则不进行更新,而是保持数据库原值。切记:至少更新一列。
<!-- 根据user的属性值有无来更新int updateBySet(Users users);--><update id="updateBySet" parameterType="users">update users<set><if test="userName != null and userName != ''">username = #{userName},</if><if test="birthday != null">birthday = #{birthday},</if><if test="sex != null and sex != ''">sex = #{sex},</if><if test="address != null and address != ''">address = #{address},</if></set>where id = #{id}</update>
注意每个if里面的语句后面需要加一个’,’逗号
标签
主要用来进行集合或数组的遍历,主要参数有:
- collection:接受过来的参数,属性值有list,array,map;它们对应的参数类型分别是:List,数组,Map集合
- item:循环体的具体对象,支持属性的点路径访问,如item.age,item.info.details,在list和数组中是其中的对象,在map中是value。
- separator:表示元素之间的分隔符,例如在in()的时候,separator=”,”会自动在元素中间用“,“隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样。该参数可选。
- open:表示该语句以什么开始
- close:表示该语句以什么结束
- index:在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选。
循环遍历参数集合
<!-- //查询所有id查List<Users> getByIds(int[] array);--><select id="getByIds" resultType="users">select<include refid="allColumn"></include>from users where id in<!--(<foreach collection="array" item="id" separator=",">#{id}</foreach>)--><foreach collection="array" item="id" separator="," open="(" close=")">#{id}</foreach></select>
批量增加
<!--//批量增加int insertBatch(List<Users> users);--><insert id="insertBatch" parameterType="users">insert into users(username,birthday,sex,address) values<foreach collection="list" item="user" separator=",">(#{user.userName},#{user.birthday},#{user.sex},#{user.address})</foreach></insert>
批量删除
<!--//批量删除int deleteBeach(int[] array);--><delete id="deleteBeach">delete from users where id in<foreach collection="array" item="id" separator="," open="(" close=")">#{id}</foreach></delete>
批量更新
注意:要使用批量更新,必须在jdbc.properties属性文件中的url中添加 &allowMultiQueries=true,允许多行操作。<!--//批量更新int updateBeach(List<Users> users);--><update id="updateBeach"><foreach collection="list" item="user" separator=";">update users<set><if test="user.userName != null and user.userName != ''">username = #{user.userName},</if><if test="user.birthday != null">birthday = #{user.birthday},</if><if test="user.sex != null and user.sex != ''">sex = #{user.sex},</if><if test="user.address!= null and user.address != ''">address = #{user.address},</if></set>where id = #{user.id}</foreach></update>
参数问题
指定参数位置
可以不使用对象的属性名进行参数值绑定,使用下标值
<!--//查询指定出生日期区间的List<Users> getBirthday(Date begin,Date end);--><select id="getBirthday" resultType="users">select <include refid="allColumn"></include>from userswhere birthday between #{arg0} and #{arg1}</select>
@Param 指定参数名称
UserMapper.java接口:
//切换列名进行模糊查询//@Param("columnName"):这里定义的columnName的名称是要在xml文件中的${引用定义的名称}//为参数添加注解List<Users> getByNameOrAddress(@Param("columnName") String columnName,@Param("columnValue") String columnValue);
UserMapper.xml中:
<select id="getByNameOrAddress" parameterType="string" resultType="users">select <include refid="allColumn"></include> from userswhere ${columnName} like concat('%',#{columnValue },'%')</select>
入参是map
入参是map,是因为当传递的数据有多个,不适合使用指定下标或指定名称的方式来进行传参,又加上参数不一定与对象的成员变量一致,考虑使用map集合来进行传递.map使用的是键值对的方式.当在sql语句中使用的时候#{键名},${键名},用的是键的名称.
<!-- //入参是mapList<Users> getByMap(Map<String,Date> map);--><select id="getByMap" resultType="users">select <include refid="allColumn"></include>from userswhere birthday between #{birthdayBegin} and #{birthdayEnd}</select>
返回值是map
<!--//返回值是map一行--><!--Map<String,Object> getReturnMap(Integer id);--><select id="getReturnMap" resultType="map" parameterType="int">select username,addressfrom users where id = #{id}</select><!--//返回值是map多行List<Map<String,Object>> getMulMap();--><select id="getMulMap" resultType="map">select username,addressfrom users</select>
列名与类中成员变量名不一致问题
- 解决方法一:使用列的别名,别名与类中的成员变量名一样,即可完成注入。
-
表的关联关系
总结:无论是什么关联关系,如果某方持有另一方的集合,则使用
标签完成映射,如果某方持有另一方的对象,则使用 标签完成映射。 一对一关系
一对多关系
<?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="com.ityg.mapper.CustomerMapper"><!--//根据客户id查询客户信息以及该客户的所有订单Customer getCustomerById(int id);实体类的属性:private Integer id;private String name;private Integer age;//该客户名下的所有订单的集合private List<Order> orders;--><resultMap id="cmap" type="customer"><!--主键绑定--><id property="id" column="cid"></id><!--非主键绑定--><result property="name" column="name"></result><result property="age" column="age"></result><!--其他实体的部分ordersprivate Integer id;private String num;private double price;--><collection property="orders" ofType="order"><id property="id" column="oid"></id><result property="num" column="orderNumber"></result><result property="price" column="orderPrice"></result></collection></resultMap><select id="getCustomerById" parameterType="int" resultMap="cmap">select c.id cid,name,age,o.id oid,orderNumber,orderPrice,customer_idfrom customer c left join orders o on c.id=o.customer_idwhere c.id = #{id}</select></mapper>
多对一关系
<?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="com.ityg.mapper.OrderMapper"><!--//根据订单id查询订单以及该订单的客户Order getOrderById(int id);实体类属性private Integer id;private String num;private double price;//关联下此订单的客户信息,多方持有一方的对象private Customer customer;--><resultMap id="omap" type="order"><id property="id" column="oid"></id><result property="num" column="orderNumber"></result><result property="price" column="orderPrice"></result><association property="customer" column="customer"><id property="id" column="cid"></id><result property="name" column="name"></result><result property="age" column="age"></result></association></resultMap><select id="getOrderById" parameterType="int" resultMap="omap">select o.id oid,orderNumber,orderPrice,c.id cid,name,agefrom orders o inner join customer c on customer_id = c.idwhere o.id = #{id}</select></mapper>
多对多关系
事务
ORM
ORM(Object Relational Mapping):对象关系映射
编写程序的时候,以面向对象的方式处理数据,保存数据的时候,却以关系型数据库的方式存储。
持久化的操作:将对象保存到关系型数据库中,将关系型数据库中的数据读取出来以对象的形式封装
