Mybatis学习02 - 基于XML的Mybatis配置
基于XML配置的CRUD操作
查询操作
在dao层接口UserDao中定义查询方法
/**
* 查询所有用户
* @return
*/
List<User> findAll();
/**
* 根据id查询用户
* @param id
* @return
*/
User findById(Integer id);
在UserDao.xml中添加配置,查询使用<select>
标签
<!--查询所有-->
<select id="findAll" resultType="com.chenpeng.domain.User">
<!--select id as userId,username as userName,birthday as userBirthday,sex as userSex,address as userAddress from users-->
select * from users
</select>
<!--根据id查询用户-->
<select id="findById" parameterType="java.lang.Integer" resultType="com.chenpeng.domain.User">
select * from users where id=#{uid}
</select>
其中<select>
标签中的属性:
- parameterType:表示方法的参数类型
- resultType:表示封装结果集的实体类的全限定类名
测试方法如下
@Test
public void testFindAll() {
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void testFindById(){
User user = userDao.findById(52);
System.out.println(user);
}
增加操作
在dao层接口UserDao中定义增加方法
/**
* 保存用户
* @param user
*/
void saveUser(User user);
在UserDao.xml中添加配置,增加使用<insert>
标签
<!--保存用户-->
<insert id="saveUser" parameterType="com.chenpeng.domain.User">
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
<!--获取插入用户的id-->
select last_insert_id()
</selectKey>
insert into users(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
其中<selectKey>
标签中的属性:
- keyProperty:表示实体类User中的属性名
- keyColumn:表示数据库中的字段名
- resultType:表示查询的返回值类型
- order:表示这条sql语句在插入之前执行还是之后执行,值有BEFORE和AFTER
测试方法如下
@Test
public void testSaveUser(){
User user = new User();
user.setUsername("cy");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("潮汕");
System.out.println("保存操作之前:"+user);
userDao.saveUser(user);
System.out.println("保存操作之后:"+user);
}
更新操作
在dao层接口UserDao中定义更新方法
/**
* 更新用户
* @param user
*/
void updateUser(User user);
在UserDao.xml中添加配置,更新使用<update>
标签
<!--更新用户-->
<update id="updateUser" parameterType="com.chenpeng.domain.User">
update users set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
</update>
测试方法如下
@Test
public void testUpdateUser(){
User user = new User();
user.setId(51);
user.setUsername("cyy");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("广东潮汕鮀浦");
userDao.updateUser(user);
}
删除操作
在dao层接口UserDao中定义删除方法
/**
* 根据id删除用户
* @param id
*/
void deleteUser(Integer id);
在UserDao.xml中添加配置,删除使用<delete>
标签
<!--删除用户-->
<delete id="deleteUser" parameterType="java.lang.Integer">
<!--此处大括号内只是一个占位符,命名没有要求-->
delete from users where id=#{uid}
</delete>
测试方法如下
@Test
public void testDeleteUser(){
userDao.deleteUser(51);
}
模糊查询
在dao层接口UserDao中定义查询方法
/**
* 根据名称模糊查询
* @param username
* @return
*/
List<User> findByName(String username);
在UserDao.xml中添加配置
<!--根据名称模糊查询-->
<select id="findByName" parameterType="String" resultType="com.chenpeng.domain.User">
<!--使用PreparedStatement的参数占位符,测试代码中必须加上%-->
select * from users where username like #{username}
<!--也可使用Statement拼接sql语句,测试代码中不用加%,但此种方式可能发生sql注入,不常用-->
<!--select * from users where username like %${value}%-->
</select>
测试方法如下
@Test
public void testFindByName(){
List<User> users = userDao.findByName("%陈%");
for (User user : users) {
System.out.println(user);
}
}
使用实体类的包装对象作为查询条件
在domain层下创建实体类QueryVo
package com.chenpeng.domain;
public class QueryVo {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
在dao层接口UserDao中定义查询方法
/**
* 根据vo模糊查询
* @param vo
* @return
*/
List<User> findByVo(QueryVo vo);
在UserDao.xml中添加配置
<!--根据vo模糊查询-->
<select id="findByVo" parameterType="com.chenpeng.domain.QueryVo" resultType="com.chenpeng.domain.User">
<!--此处大括号内不能直接写username,因为QueryVo类中没有getUsername()方法,所以可以使用OGNL 表达式,使用类名.属性名的方式获取-->
select * from users where username like #{user.username}
</select>
测试方法如下
@Test
public void testFindByVo(){
QueryVo vo = new QueryVo();
User user = new User();
user.setUsername("%陈%");
vo.setUser(user);
List<User> users = userDao.findByVo(vo);
for (User u : users) {
System.out.println(u);
}
}
调整实体类属性解决属性名和数据库中字段名不对应问题
修改实体类User
package com.chenpeng.domain;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getUserBirthday() {
return userBirthday;
}
public void setUserBirthday(Date userBirthday) {
this.userBirthday = userBirthday;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public String getUserAddress() {
return userAddress;
}
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", userBirthday=" + userBirthday +
", userSex='" + userSex + '\'' +
", userAddress='" + userAddress + '\'' +
'}';
}
}
此时实体类中属性名和数据库中字段名不对应了,再次测试会发现封装结果集时只有username有值,其他均为null,因为Windows环境下的Mysql数据库不区分大小写(Linux环境下区分大小写)
问题解决
1.修改sql语句,例如
select id as userId,username as userName,birthday as userBirthday,sex as userSex,address as userAddress from users
但是这种方式需要修改之前写好的sql语句,非常麻烦
2.配置查询结果的列名和实体类的属性名的对应关系
在UserDao.xml中添加配置
<resultMap id="userMap" type="com.chenpeng.domain.User">
<!--主键字段的对应-->
<id property="userId" column="id"></id>
<!--非主键字段的对应-->
<result property="userName" column="username"></result>
<result property="userBirthday" column="birthday"></result>
<result property="userSex" column="sex"></result>
<result property="userAddress" column="address"></result>
</resultMap>
同时修改<select>
标签中的属性resultType为resultMap
<select id="findAll" resultMap="userMap">
select * from users
</select>
这种方式不用修改sql语句,只需将resultType属性改为resultMap属性即可,更加方便
Mybatis配置文件中的其他标签的使用
properties
标签
在<properties>
标签中配置连接数据库的信息,有两种方式配置
-
使用
<properties>
标签直接配置<properties> resource="jdbcConfig.properties"--> <!--<property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=utf-8&useSSL=false"/> <property name="username" value="用户名"/> <property name="password" value="密码"/> </properties> <!--配置数据源(连接池)--> <dataSource type="POOLED"> <property name="driver" value="driver"/> <property name="url" value="url"/> <property name="username" value="username"/> <property name="password" value="password"/> </dataSource>
-
使用
<properties>
标签中的resource属性或url属性resource属性用于指定配置文件的位置,按照类路径的写法来写,并且必须存在于类路径下
<properties resource="jdbcConfig.properties>
或者
修改数据源配置
其中jdbcConfig.properties文件在resources目录下,内容如下
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=utf-8&useSSL=false
jdbc.username=用户名
jdbc.password=密码
typeAliases
标签
在<typeAliases>
标签中可以为实体类配置别名,有两种方式配置
-
使用
<typeQliases>
标签为单个实体类配置别名<typeAliases> <typeAlias type="com.chenpeng.domain.User" alias="user"></typeAlias> </typeAliases>
其中typeAlias中的属性:
- type:表示实体类的全限定类名
- alias:表示实体类的别名,不区分大小写
-
使用
<package>
标签为整个包内的实体类配置别名,配置后实体类的类名就是别名,不区分大小写<typeAliases> <package name="com.chenpeng.domain"/> </typeAliases>
mappers
标签
在<mappers>
标签中可以配置映射文件的位置,有两种方式配置
-
使用
<mapper>
标签中的resource属性指定映射文件的位置<mappers> <mapper resource="com/chenpeng/dao/UserDao.xml"/> </mappers>
-
使用
<package>
标签指定UserDao接口所在的包,从而找到UserDao接口的映射文件<mappers> <package name="com.chenpeng.dao"/> </mappers>
动态sql语句
if
标签 - 执行判断
在dao层接口UserDao中定义查询方法
/**
* 根据条件查询
* @param user
* @return
*/
List<User> findByCondition(User user);
在UserDao.xml中添加配置
<select id="findByCondition" parameterType="user" resultMap="userMap">
select * from users where 1=1
<!--如果user对象的userName不为空,就加上username的判断条件-->
<if test="userName != null">
and username = #{userName}
</if>
</select>
测试方法如下
@Test
public void testFindByCondition(){
User user = new User();
user.setUserName("陈宇宇宇");
List<User> users = userDao.findByCondition(user);
for (User user1 : users) {
System.out.println(user1);
}
}
where
标签 - 给sql语句加上where条件判断
上述例子中进行条件判断时加上了where 1 = 1,以防止传入的user对象为空,也可以使用<where>
标签,它可以根据传入的实体类对象判断是否加上where语句
<select id="findByCondition" parameterType="user" resultMap="userMap">
select * from users
<where>
<if test="userName != null">
and username = #{userName}
</if>
</where>
</select>
foreach
标签 - 遍历集合
当要进行聚合查询时(即in/not in查询),可以使用<foreach>
标签
在dao层接口UserDao中定义查询方法
/**
* 根据QueryVO中提供的id集合,查询用户信息
* @param vo
* @return
*/
List<User> findUserInIds(QueryVo vo);
在UserDao.xml中添加配置
<!--根据id集合查询-->
<select id="findUserInIds" resultMap="userMap" parameterType="queryvo">
select * from users
<where>
<if test="ids != null and ids.size() > 0">
<!--item中的值跟下面#{}内的值对应-->
<foreach collection="ids" open="and id in (" close=")" item="id" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
其中<foreach>
标签中的属性:
- collection:表示要遍历的集合对象
- open:遍历的sql以什么开头
- close:遍历的sql以什么结束
- separator:分隔符,遍历一次后在集合中的对象之间加的字符
- item:表示每次遍历的元素
测试方法如下
@Test
public void testFindUserInIds(){
QueryVo vo = new QueryVo();
List<Integer> list = new ArrayList<Integer>();
list.add(52);
list.add(53);
list.add(56);
list.add(57);
list.add(58);
list.add(59);
vo.setIds(list);
List<User> user = userDao.findUserInIds(vo);
for (User user1 : user) {
System.out.println(user1);
}
}
sql
标签 - 抽取sql语句中的重复语句
每个方法的sql语句中,有大量重复的部分,可以使用<sql>
标签定义sql语句,使用<include>
标签引用<sql>
标签中的sql语句
<sql id="defaultSelect">
select * from users
</sql>
<select id="findAll" resultMap="userMap">
<include refid="defaultSelect"/>
</select>