Mybatis系列文章
1. 添加操作
1.1 在 UserMapper 接口中新增 saveUser()
方法
public interface UserMapper {
/**
* 查询所有用户
*
* @return
*/
List<User> listAllUsers();
/**
* 添加用户
* @param user
* @return 成功返回1,失败返回0
*/
int saveUser(User user);
}
1.2 在 Mapper 映射文件 UserMapper.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="cn.ykf.mapper.UserMapper">
<!-- 配置查询所有用户 -->
<select id="listAllUsers" resultType="cn.ykf.pojo.User">
SELECT * FROM user
</select>
<!-- 添加用户 -->
<insert id="saveUser" parameterType="cn.ykf.pojo.User">
INSERT INTO user(username,birthday,sex,address) VALUES (#{username},#{birthday},#{sex},#{address})
</insert>
</mapper>
- 温馨提示
- 在编写映射文件的时候,可以先在 MySQL 客户端写好可以正常运行的 SQL 语句,然后将其搬运到映射文件中,并把原本的实际参数都替换为 Mybatis 的参数符号
parameterType
表示将会传入这条语句的参数类的完全限定名或别名。- 其中的参数符号
#{username}
,#{birthday}
,#{birthday}
,#{sex}
,#{address}
必须和User类的每个属性名一一对应,不可以乱写。
- 属性名是实体类的
getXxx()/setXxx()
中Xxx
部份,大多数情况下就是成员变量名,也有少数情况不是成员变量名,也就是说成员变量和属性不能等同。- 关于配置文件的相关说明,可以查看官网文档:Mybatis的XML映射文件
1.3 测试添加操作
public class MybatisTest {
private InputStream is;
private SqlSession sqlSession;
private UserMapper mapper;
/**
* 测试之前执行,用于初始化
*/
@Before
public void init() throws Exception {
// 1. 读取配置文件
is = Resources.getResourceAsStream("mybatis-config.xml");
// 2. 创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
// 3. 获取SqlSession对象
sqlSession = factory.openSession();
// 4. 使用SqlSession创建Mapper的代理对象
mapper = sqlSession.getMapper(UserMapper.class);
}
/**
* 测试结束执行,用于提交事务和释放资源
*/
@After
public void destroy() throws Exception {
// 6. 提交事务
sqlSession.commit();
// 7. 释放资源
sqlSession.close();
is.close();
}
/**
* 测试添加用户
*/
@Test
public void testSaveUser() {
User user = new User();
user.setUsername("鱼开饭");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("广东");
// 调用mapper完成添加
int count = mapper.saveUser(user);
System.out.println("添加条数为 : " + count);
}
}
- 这里有一点需要注意,如果在
destory()
方法中没有通过sqlSession.commit();
来提交事务的话,那么添加的记录不会写入到数据库中。- 因为我们在调用
openSession()
时并没有设置事务自动提交,所以最终事务会自动回滚,导致记录没有写入到数据库中。见下图
openSession()
的重载方法如下图,因此,如果需要自动提交事务,只需factory.openSession(true);
即可。
1.4 测试结果
2. 删除操作
2.1 在 UserMapper 接口中新增 removeUserById()
方法
/**
* 根据id删除用户
*
* @param userId
* @return 成功返回1,失败返回0
*/
int removeUserById(Integer userId);
2.2 在 Mapper 映射文件 UserMapper.xml
中配置删除操作
<!-- 删除用户 -->
<delete id="removeUserById" parameterType="java.lang.Integer">
DELETE FROM user WHERE id = #{uid}
</delete>
- 这里有两点需要注意一下!
- 第一点,如果参数是基本类型或者基本类型的包装类,且只有一个参数,那么参数符号可以随便写 。也就是说,虽然 Mapper 接口中的方法声明为
int removeUserById(Integer userId)
,但是映射文件中既可以写#{userId}
,也可以写#{aaaa}
。- 第二点,
parameterType
写int
、INT
、INTEGER
、integer
、java.lang.Integer
都可以。原因可以看 6.2 typeAliases 标签
2.3 测试删除操作
/**
* 测试删除用户
*/
@Test
public void testremoveUserById(){
// 这里传的id为自己数据库中存在的id值
int count = mapper.removeUserById(50);
System.out.println("删除条数为 : " + count);
}
2.4 测试结果
3. 修改操作
3.1 在 UserMapper 接口中新增 updateUser()
方法
/**
* 修改用户
* @param user
* @return 成功返回1,失败返回0
*/
int updateUser(User user);
3.2 在 Mapper 映射文件 UserMapper.xml
中配置修改操作
<!-- 修改用户 -->
<update id="updateUser" parameterType="cn.ykf.pojo.User">
UPDATE user SET username = #{username}, birthday = #{birthday}, sex = #{sex}, address = #{address} WHERE id = #{id}
</update>
3.3 测试修改操作
/**
* 测试修改用户
*/
@Test
public void testUpdateUser() {
// 因为还没有编写根据id查询用户,所以模拟数据
User user = new User();
user.setUsername("update");
user.setAddress("test");
user.setSex("女");
user.setBirthday(new Date());
// id 为自己数据库中存在的值
user.setId(49);
// 执行修改
int count = mapper.updateUser(user);
System.out.println("修改条数为 : " + count);
}
3.4 测试结果
4. 查询操作
4.1 根据id查询
- 在
UserMapper
中添加方法
/**
* 根据id查询单个用户
* @param userId
* @return
*/
User getUserById(Integer userId);
- 修改映射文件
<!-- 查询单个用户 -->
<select id="getUserById" parameterType="java.lang.Integer" resultType="cn.ykf.pojo.User">
SELECT * FROM user WHERE id = #{uid}
</select>
- 测试代码及结果
/**
* 测试查询单个用户
*/
@Test
public void testGetUserById() {
// 确保id存在,否则返回null
User user = mapper.getUserById(48);
System.out.println(user);
}
4.2 模糊查询
- 在
UserMapper
中添加方法
/**
* 根据姓名模糊查询多个用户
*
* @param username
* @return
*/
List<User> listUsersByName(String username);
- 修改映射文件
<!-- 根据姓名模糊查询多个用户 -->
<select id="listUsersByName" parameterType="java.lang.String" resultType="cn.ykf.pojo.User">
SELECT * FROM user WHERE username LIKE #{name}
</select>
- 虽然
UserMapper
中方法的返回值为List<User>
,但是映射文件中resultType
写User
就行。因为如果有多条记录的话,Mybatis 会自动帮我们封装成一个 List 集合。- 这里的
parameterType
也可以直接写String
。- 参数符号可以随便写。
- 测试代码及结果
/**
* 测试模糊查询
*/
@Test
public void testListUsersByName() {
List<User> users = mapper.listUsersByName("%王%");
// 使用 Stream 流 + 方法引用,需要至少jdk8
users.forEach(System.out::println);
}
- 如果出现了
There is no getter of 'name' in java.lang.string
的错误,请参考链接:mybatis中传入String类型参数的问题- 由于映射文件中的 SQL 语句并没有对参数进行模糊查询处理,所以在调用方法的时候我们必须手动为查询的关键字进行
%
拼接,这样很不方便。如果想调用方法查询时,只传入查询的关键字,那么可以采用以下方法 :
- 在 SQL 语句中,使用 MySQL 默认提供的函数
concat()
进行拼接SELECT * FROM user WHERE username LIKE concat('%',#{name},'%');
- 在 SQL 语句中手动进行拼接
SELECT * FROM user WHERE username LIKE "%"#{name}"%";
4.3 使用聚合函数查询用户总数
- 在
UserMapper
中添加方法
/**
* 查询用户总数
*
* @return
*/
int countUser();
- 修改映射文件
<!-- 查询用户总数 -->
<select id="countUser" resultType="int">
SELECT count(id) FROM user
</select>
- 测试代码及运行结果
/**
* 测试查询用户总数
*/
@Test
public void testCountUser() {
int count = mapper.countUser();
System.out.println("用户总记录数为 : " + count);
}
5. 扩展操作
5.1 添加用户时获取 id 的返回值
- 在添加用户的时候,如果想获取新增用户的 id 值,那么就可以使用
<selectKey></selectKey>
标签,见代码
<!-- 添加用户,同时获取 id 的返回值 -->
<insert id="saveUser" parameterType="cn.ykf.pojo.User">
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO user(username,birthday,sex,address) VALUES (#{username},#{birthday},#{sex},#{address})
</insert>
keyProperty
表示 selectKey 语句结果应该被设置的目标属性(对应实体类)。keyColumn
表示匹配属性的返回结果集中的列名称(对应数据库结果集)。order
可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先生成主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后再执行 selectKey 中的语句。
- 测试代码及运行结果
/**
* 测试添加用户,并获取 id 的返回值
*/
@Test
public void testSaveUser() {
User user = new User();
user.setUsername("鱼开饭");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("广东");
System.out.println("添加前 : " + user);
// 调用mapper完成添加
int count = mapper.saveUser(user);
System.out.println("添加条数为 : " + count);
System.out.println("添加后 : " + user);
}
5.2 使用 pojo 包装类进行查询
- 首先介绍一下 OGNL 表达式
- 全称
Object Graphic Navigation Language
,即对象图导航语言 - 它是通过对象的取值方法来获取数据。在写法上把get给省略了。
- 全称
- 在开发中如果想实现复杂查询 ,查询条件不仅包括用户查询条件,还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用 pojo 包装对象传递输入参数
- 编写
QueryVo
类来封装查询条件
/**
* 用于封装查询条件
*
* @author yukaifan
* @ClassName QueryVo
* @date 2020/2/1 11:04
*/
public class QueryVo implements Serializable {
private User user;
// 如果还有其他的查询条件,就可以一并封装进来
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
- 编写接口层代码和映射文件
/**
* 根据查询条件模糊查询用户
*
* @param vo
* @return
*/
List<User> listUsersByVo(QueryVo vo);
<!-- 根据查询条件模糊查询 -->
<select id="listUsersByVo" parameterType="cn.ykf.pojo.QueryVo" resultType="cn.ykf.pojo.User">
SELECT * FROM user WHERE username LIKE CONCAT('%',#{user.username},'%')
</select>
- 测试代码及运行结果
/**
* 测试根据Vo查询
*/
@Test
public void testListUsersByVo(){
QueryVo vo = new QueryVo();
User user = new User();
user.setUsername("王");
vo.setUser(user);
List<User> users = mapper.listUsersByVo(vo);
users.forEach(System.out::println);
}
5.3 使用 resultMap 接收查询结果
- 在某些情况下,实体类的属性和数据库表的列名并不一致,那么就会出现查询结果无法封装进实体类。修改实体类进行演示:
public class User implements Serializable {
// 此时实体类属性与数据库表的列表已经不一致了
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
// 此处不展示 getter()/setter()...
// 此处不展示 toString()...
}
- 修改映射文件中的参数符号,并测试原本的查询方法
listAllUsers()
,运行结果如下
原本应该所有的属性都为 null ,但是由于在 Windows 环境下 MySQL 不区分大小,所以
userName
等同username
,不过要注意在 Linux 环境下 MySQL 严格区别大小写。
- 为了解决实体类属性名与数据库表列名不一致,有以下解决方法:
- 在 SQL 语句中为所有列起别名,使别名与实体类属性名一致(执行效率相对高,开发效率低)
<select id="listAllUsers" resultType="cn.ykf.pojo.User">
SELECT id AS userId, username AS userName, birthday AS userBirthday, sex AS userSex, address AS userAddress FROM user
</select>
- 使用 resultMap 完成结果集到实体类的映射(执行效率相对低,开发效率高)
<mapper namespace="cn.ykf.mapper.UserMapper">
<!-- 配置 resultMap ,完成实体类与数据库表的映射 -->
<resultMap id="userMap" type="cn.ykf.pojo.User">
<id property="userId" column="id" />
<result property="userName" column="username"/>
<result property="userBirthday" column="birthday"/>
<result property="userAddress" column="address"/>
<result property="userSex" column="sex"/>
</resultMap>
<!-- 配置查询所有用户 -->
<select id="listAllUsers" resultMap="userMap">
SELECT * FROM user
</select>
</mapper>
6. Mybatis 配置文件标签讲解
6.1 properties
标签
- 在配置数据库连接的时候,我们可以采用以下几种方式来配置:
- 第一种,采用全局的内部配置。采用这种方式的话,如果需要配置多个数据库环境,那么像
username、password
等属性就可以复用,提高开发效率。
<?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>
<!-- 全局变量 -->
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db_mybatis"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
<!--配置环境-->
<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>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
- 第二种,使用
resources
属性引入外部配置文件(常用)
编写配置文件
jdbcConfig.properties
。配置文件名没有限制,但是配置文件一定要放在类路径下
# 键为 jdbc.xxx 可以自行修改
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_mybatis
jdbc.username=root
jdbc.password=123456
修改 Mybatis 配置文件
<!-- 引入外部文件 -->
<properties resource="jdbcConfig.properties"/>
<!--配置环境-->
<environments default="development">
<environment id="development">
<!-- 配置事务类型 -->
<transactionManager type="JDBC"/>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
- 第三种,使用
url
属性引入外部配置文件
该方法的外部文件可以放在任意位置,但是路径写法必须按照 Url 的方式,写起来比较麻烦,推荐第二种方法
<!-- 引入外部文件 -->
<properties url="file:///D:/document/IdeaProjects/java_web_ssm/my_mybatis/src/main/resources/jdbcConfig.properties"/>
- URL:Uniform Resouce LOcator,即统一资源定位符。它可以唯一标识一个资源的位置,由四部分组成:协议、主机、端口、路径。
- 例如:http://localhost:8080/mybatisserver/demo1,其中
http
为协议,localhost
为主机,8080
为端口号,/mybatisserver/demo1
为uri(路径)- URI:Uniform Resource Identifier,即统一资源标识符。它是在应用中可以唯一定位一个资源的。
6.2 typeAliases
标签
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
- 如果我们也想给某个实体类指定别名的时候,就可以采用
typeAliases
标签。用法如下:
<configuration>
<!--配置别名-->
<typeAliases>
<typeAlias type="cn.ykf.pojo.User" alias="user"/>
</typeAliases>
<!-- 其他配置省略... -->
</configuration>
typeAlias
子标签用于配置别名。其中type
属性用于指定要配置的类的全限定类名(该类只能是某个domain实体类),alias
属性指定别名。一旦指定了别名,那么别名就不再区分大小写。- 也就是说,此时我们可以在映射文件中这样写
resultType="user"
,也可以写resultType="USER"
。
- 当我们有多个实体类需要起别名的时候,那么我们就可以使用
package
标签。
<typeAliases>
<!-- 包下所有实体类起别名 -->
<package name="cn.ykf.pojo"/>
</typeAliases>
package
标签指定要配置别名的包,当指定之后,该包下的所有实体类都会注册别名,并且别名就是类名,不再区分大小写
package
标签还可以将某个包内的映射器接口实现全部注册为映射器,如下所示
<!-- 指定映射文件 -->
<mappers>
<package name="cn.ykf.mapper"/>
</mappers>
- 这样配置后,我们就无需一个一个地配置 Mapper 接口了。不过,这种配置方式的前提是映射配置文件位置必须和dao接口的包结构相同,如图所示
- 关于更多 XML 配置文件,请参考官方文档:Mybatis XML配置文件