文章目录
1. 环境快速搭建
-
创建一个maven项目
-
导入mybatis坐标依赖
<!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <!-- mysql 驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> <!-- 添加slf4j日志api --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.20</version> </dependency> <!-- 添加logback-classic依赖 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <!-- 添加logback-core依赖 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency>
-
创建一张测试使用的数据库表并且插入相应数据
create database mybatis; use mybatis; drop table if exists tb_user; create table tb_user( id int primary key auto_increment, username varchar(20), password varchar(20), gender char(1), addr varchar(30) ); INSERT INTO tb_user VALUES (1, 'zhangsan', '123', '男', '北京'); INSERT INTO tb_user VALUES (2, '李四', '234', '女', '天津'); INSERT INTO tb_user VALUES (3, '王五', '11', '男', '西安');
-
添加日志配置文件到resources文件夹下
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- CONSOLE :表示当前的日志信息是可以输出到控制台的。 --> <appender name="Console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>[%level] %blue(%d{HH:mm:ss.SSS}) %cyan([%thread]) %boldGreen(%logger{15}) - %msg %n</pattern> </encoder> </appender> <logger name="cn.edu.xd" level="DEBUG" additivity="false"> <appender-ref ref="Console"/> </logger> <!-- level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF , 默认debug <root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。 --> <root level="DEBUG"> <appender-ref ref="Console"/> </root> </configuration>
-
编写和数据表对应的实体类
public class Brand { // id 主键 private Integer id; // 品牌名称 private String brandName; // 企业名称 private String companyName; // 排序字段 private Integer ordered; // 描述信息 private String description; // 状态:0:禁用 1:启用 private Integer status; //省略set get toString方法 }
-
编写mybatis-config.xml配置文件,放置于resources文件夹下
<?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:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment --> <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:///mybatis?useSSL=false&serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="123"/> </dataSource> </environment> </environments> <mappers> <!--加载sql映射文件--> <mapper resource="UserMapper.xml"/> </mappers> </configuration>
-
测试代码
@Test public void testMybatisUse() throws IOException { String path="mybatis-config.xml"; InputStream inputStream= Resources.getResourceAsStream(path); SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(inputStream);//1。 获取SqlSessionFactory //2. 获取SqlSession对象 SqlSession session=factory.openSession(); //3. 执行sql List<User> users = session.selectList("test.selectAll"); System.out.println(users); session.close();//关闭会话 }
第7步中代码的执行是一种比较简单是使用 session.selectList(“test.selectAll”); test是mybatis配置文件中的命令空间 selectAll是该命名空间下写的一个sql方法 实际上在开发中使用的Mapper接口开发
修改如下:
添加一个UserMapper接口(cn.edu.xd.UserMapper):
public interface UserMapper { List<User> selectAll(); }
mybatis配置文件中的命名空间和接口的包名一致,sql查询语句的id和接口中的方法名一致,注意: 接口和对应的mapper.xml文件需要放在同一个文件夹下,可以直接将UserMapper.xml文件放在UserMapper接口所在的包下,也可以在resources下面建立和包名层级相同的文件目录,将UserMapper.xml文件放在该目录下
<mapper namespace="cn.edu.xd.mapper.UserMapper"> <select id="selectAll" resultType="cn.edu.xd.pojo.User"> select * from tb_user; </select> </mapper>
此时mybatis-config.xml中加载mapper.xml的路径改写如下:
<mappers> <!--加载sql映射文件--> <mapper resource="cn/edu/xd/mapper/UserMapper.xml"/> </mappers>
2. 对象属性封装问题
数据库中有一个tb_user表 java代码中有一个User实体类 如果实体类的属性的名字和数据表一致,那么在查询时可以将从数据库表中查询到的数据封装到实体类的对象中,但是如果某个属性名和列名不一致,该属性将会时null值
eg: User中的是userName tb_user表中的是username 执行结果如下:
解决方案1: sql语句中取别名
<select id="selectAll" resultType="cn.edu.xd.pojo.User">
select id, username name,password passwd,gender,addr from tb_user where id=1;
</select>
// 将不一致的列名和属性通过取别名修改和属性名一致
解决方案2:使用ResultMap封装**(推荐)**
在UserMapper.xml文件中,和查询语句在同一命名空间下添加以下内容:
<resultMap id="userMap" type="cn.edu.xd.pojo.User">
<!--
id:完成主键字段的映射
column:表的列名
property:实体类的属性名
result:完成一般字段的映射
column:表的列名
property:实体类的属性名
只需要映射不匹配的字段和列名
-->
<id column="id" property="id"/>
<result column="username" property="name"/>
<result column="password" property="passwd"/>
</resultMap>
之后查询语句的返回类型需要修改:
<select id="selectAll" resultMap="userMap"> # 之前是resultType
select * from tb_user where id=1;
</select>
3. 实体类别名
在编写mapper.xml文件时,每次都要写完整的类路径名比较麻烦,可以在mybatis-config文件夹中开启包扫描,这样改包下面的所有的类都可以直接使用类型,大小写不区分User user useR都可以
mybati-config.xml:
<typeAliases>
<package name="cn.edu.xd.pojo"/>
</typeAliases>
以后再mapper.xml文件中涉及返回类型时实体类时就可以直接使用user
4. 普通查询
4.1 查询所有
在前面的selectAll()方法中已经介绍过
4.2 单个条件查询
UserMapper接口中条件一个方法:User selectById(Integer id);
UserMapper.xml文件条件:
<select id="selectById" resultMap="userMap">
select * from tb_user where id=#{id};
</select>
// 这里只有1个参数进行传递 #{id}中的id可以写成任意名字
条件查询当涉及<(小于)时,xml文件会将<作为一个特殊字符而报错,因此需要对特殊字符进行转义,有2种转义方法:
- < 使用 $lt; 代替
- 使用CDATA标签
5. 多条件查询
需求:按地址和性别查询,比如查询在西安的男性
这里涉及到了多条件查询,因此需要向sql语句中传递多个参数,因此介绍几种传递多个参数的方式
- 使用 @Param(“参数名称”) 标记每一个参数,在映射配置文件中就需要使用 #{参数名称} 进行占位
- 将多个参数封装成一个 实体对象 ,将该实体对象作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和实体类属性名保持一致
- 将多个参数封装到map集合中,将map集合作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容}时,里面的内容必须和map集合中键的名称一致
方式1:
接口方法:List<User> selectByGenderAndAddr(@Param("gender") String gender, @Param("addr") String addr);
mapper文件:
<select id="selectByGenderAndAddr" resultMap="userMap">
select * from tb_user where gender=#{gender} and addr=#{addr};
</select>
// {}中的参数名称必须和@Param注解中的参数名称一致
测试方法:
List<User> users=userMapper.selectByGenderAndAddr(gender,addr);
方式2:
接口方法:List<User> selectByGenderAndAddr(User user);
mapper文件:
<select id="selectByGenderAndAddr" resultMap="userMap">
select * from tb_user where gender=#{gender} and addr=#{addr};
</select>
// {}中的参数必须和User类中的属性名一致
测试方法:
User user=new User();
user.setGender("男");
user.setAddr("西安");
List<User> users=userMapper.selectByGenderAndAddr(user);
方式3:
接口方法:List<User> selectByGenderAndAddr(HashMap<String,String> map);
mapper文件:
<select id="selectByGenderAndAddr" resultMap="userMap">
select * from tb_user where gender=#{sex} and addr=#{city};
</select>
// {}中的参数必须和放入map时的key相同
测试方法:
HashMap<String,String> map=new HashMap<>();
map.put("sex",gender);
map.put("city",addr);
List<User> users=userMapper.selectByGenderAndAddr(map);
6. 添加数据
- 将数据封装成一个对象
- 将对象作为参数传递给sql查询
- sql查询中的{}中的占位符名字必须和类中的属性一致
接口方法:
void add(User user);
maper.xml文件
<insert id="add">
insert into tb_user values (#{id},#{name},#{passwd},#{gender},#{addr});
</insert>
# {}中的占位符名字必须和类中的属性一致
测试方法:
User user=new User();
user.setId(4);
user.setName("mike");
user.setPasswd("9999");
user.setGender("男");
user.setAddr("西安");
userMapper.add(user);
System.out.println("添加数据成功");
session.commit();//默认事务是关闭的 需要手动提交
// 记得提交事务
添加数据并且返回该数据在数据表中的主键:
修改mapper.xml文件:
<insert id="add" useGeneratedKeys="true" keyProperty="id">
insert into tb_user values (#{id},#{name},#{passwd},#{gender},#{addr});
</insert>
// useGeneratedKeys:是够获取自动增长的主键值。true表示获取
// keyProperty :指定将获取到的主键值封装到对象的哪个属性里
测试代码:
userMapper.add(user);
Integer id = user.getId();
System.out.println(id);
7. 修改数据
场景:修改用户的密码都为9999
接口方法:int updatePasswd(String passwd);
maper.xml:
<update id="updatePasswd">
update tb_user set password=#{passwd};
</update>
测试方法:
userMapper.updatePasswd("999");
8. 删除数据
8.1 单条数据删除
接口方法:int deleteById(int id);
maper.xml:
<delete id="deleteById">
delete from tb_user where id=#{id};
</delete>
测试方法:
userMapper.deleteById(4);
8.2 多条数据删除
接口方法:
int deleteByIds(@Param("ids") int[] ids);
这里@Param注解设置的参数就是后面使用foeach遍历处需要写的变量名称
maper.xml:
<delete id="deleteByIds">
delete from tb_user where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach
</delete>
# collection="ids" ids和@Param中的参数名一致 如果@Param没有设置 这里写array
测试方法:
int[] ids={2,3};
userMapper.deleteByIds(ids);
9. Mybatis参数传递
- 多个参数
- 单个参数
- POJO 类型Map 集合类型
- Collection 集合类型
- List 集合类型
- Array 类型
- 其他类型
mybatis封装参数的原理:自动将参数封装进map, 传入的值作为value, key是什么分为以下两种情况
-
如果没有使用@Param注解,此时map的key是arg0 arg1…或者param1 param2…
List<User> selectByGenderAndAddr(String gender, String addr); 如果要想在sql语句中使用到gender和addr的值,有以下两种方式: <select id="selectByGenderAndAddr" resultMap="userMap"> select * from tb_user where gender=#{arg0} and addr=#{arg1}; </select> 或者 <select id="selectByGenderAndAddr" resultMap="userMap"> select * from tb_user where gender=#{param1} and addr=#{param2}; </select>
-
使用@Param注解, 此时默认的key就会失效 注解中设置的变量名为map的key
不同类型的单个参数处理:
-
POJO 普通对象类型: 直接使用 需要注意#{xxx}中的变量名需要和实体类中的属性一致
-
Map集合类型:直接使用 需要注意#{xxx}中的变量名需要和map放数据时中的key名字一致
-
Collection类型:Mybatis 会将集合封装到 map 集合中
map.put("arg0",collection集合);//collection集合是第一个参数 map.put("collction",collection集合);
可以使用 @Param 注解替换map集合中默认的 arg 键名
-
List类型:Mybatis 会将集合封装到 map 集合中,如下:
map.put("arg0",List集合);//list集合是第一个参数 map.put("collction",List集合); map.put("list",List集合);
-
Array数组类型:Mybatis 会将集合封装到 map 集合中,如下:
map.put("arg0",数组名);//数组是第一个参数 map.put("array",数组名); //所以在前面批量删除时 传入的ids数组 在foreach中遍历时指定的集合名是array
collection list array这几种类型都可以使用@Param绑定参数作为key
一个问题:当map或者list或者array中的数据是对象类型时,如何使用?
拿前面的按id查询为例:
原来的接口:selectById(int id)
现在修改为:selectById(Hash<String,User> map);
mapper.xml文件:
<select id="selectById" resultMap="userMap">
select * from tb_user where id=#{user.id};
</select>
// value时对象类型 需要的是对象的属性 那就使用.来访问
测试方法:
HashMap<String,User> map=new HashMap<>();
User user=new User();
user.setId(1);
map.put("user",user);
user=userMapper.selectById(map);
10. 动态sql
10.1 动态查询
场景:按性别、城市查找用户 但是这两个条件可以只有1个 也可以一个也没有
接口方法:
List<User> dynamicSelect(@Param("sex") String gender,@Param("city") String addr);
mapper.xml文件:
<select id="dynamicSelect" resultMap="userMap">
select * from tb_user
<where>
<if test="sex!=null">
and gender=#{sex}
</if>
<if test="city!=null">
and addr=#{city}
</if>
</where>
</select>
// 如果两个条件都是null 就相当于查询所有
if标签可以用来判断传入的查询参数是否为null where标签可以防止某个条件不成立时导致的sql语法错误 比如当sex为null时 第一个if没有 如果使用where而不是where标签,就会出现
select * from tb_user and addr=#{city}的语法错误
测试方法:
String gender=null;
String addr=null;
//gender="男";
addr="西安";
List<User> users=userMapper.dynamicSelect(gender,addr);
System.out.println(users);
//gender addr可以任意选取
10.2 动态删除
场景:按gender或者addr删除用户 两个条件可能有0个 1个 2个
接口方法:
int dynamicDelete(@Param("sex") String gender,@Param("city") String addr);
mapper.xml文件:
<delete id="dynamicDelete">
delete from tb_user
<where>
<if test="sex!=null">
or gender=#{sex}
</if>
<if test="city!=null">
or addr=#{city}
</if>
</where>
</delete>
// 如果两个条件都是null 就相当于删除所有
测试方法:
String gender=null;
String addr=null;
//gender="男";
addr="西安";
userMapper.dynamicDelete(gender,addr);
System.out.println("删除成功");
10.3 动态更新
场景:给定性别和地址两个输入框 用户可以输入任意一个 两个 也可以不输入 对用户数据进行更新
接口方法:
int dynamicUpdate(@Param("sex") String gender,@Param("city") String addr,@Param("id") int id);
mapper.xml文件:
<update id="dynamicUpdate">
update tb_user
<set>
<if test="sex!=null">
gender=#{sex},
</if>
<if test="city!=null">
addr=#{city},
</if>
</set>
where id=#{id}
</update>
// 需要使用set标签来过滤一些可能出现的语法错误 注意当两个条件都为null时会出现语法错误
测试方法:
String gender=null;
String addr=null;
//gender="男";
//addr="上海";
userMapper.dynamicUpdate(gender,addr,1);
System.out.println("修改成功");
11. 注解开发
4种注解:
- @Select
- @Insert
- @Update
- @Delete
使用:在对应的接口方法上面加上对应的注解,在()中写入sql语句
eg:
接口方法:
@Select("select * from tb_user")
List<User> selectAll();
//注解和xml文件中的查询语句只能有1个 不然会报错
测试方法:
UserMapper userMapper=session.getMapper(UserMapper.class);
List<User> users = userMapper.selectAll();
注解开发虽然方便 但是只适合比较简答的sql查询 比如前面使用的动态查询 如果写在注解中就会显得很臃肿