之前在a_26_0学MyBatis快速入门的时候,讲了映射文件简单的增删改查,这节课将深入学习MyBatis的映射文件 学这节课之前,请你保证已经对上节课dao层的代理开发方式很熟悉,因为这节课我们写接口不写对应的实现类,也就是使用代理开发方式简化掉实现类 并且还要熟悉前面a_27学的增删改查
MyBatis的映射文件中,前面我们的SQL都是比较简单的,有些时候业务逻辑复杂时,我们的SQL是动态变化的,也就是会根据业务层传递给dao层的参数不同, 导致SQL语句也不同,此时在前面的学习中就不能满足要求了
具体操作如下
第一步: 在src/main/java目录下,右键java,新建com.huanf.domain.User类,写入如下
//数据库中的user表数据
public class User {
//对应表中的字段
private int id;
private String username;
private String password;
//get和set方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
第二步: 在src/main/java目录下,右键java,新建com.huanf.mapper.UserMapper接口,写入如下
public interface UserMapper {
public List<User> findByCondition(User user);
}
第三步: 在src/main/resources目录下新建jdbc.properties文件,写入如下
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=228675
第四步: 在src/main/resources目录下新建log4j.properties文件,写入如下
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=debug, stdout
第五步: 在src/main/resources目录下新建sqlMapConfig.xml文件,写入如下
<configuration>
<!--通过properties标签加载外部(resources目录)配置的properties文件-->
<properties resource="jdbc.properties"></properties>
<!--为UserMapper.xml里面的resultType、parameterType属性需要的全限定名,设置别名为user-->
<typeAliases>
<typeAlias type="com.huanf.domain.User" alias="user"></typeAlias>
</typeAliases>
<!--配置数据源环境。default表示默认情况下使用的是哪个环境,例如development,环境名是自定义的-->
<!--transactionManager指的是你要用哪种事务管理器,例如原生JDBC。dataSource指的是你要用哪种数据源,例如连接池POOLED-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<!--下面那行的'?useSSL=false&useUnicode=true&characterEncoding=UTF-8'可写可不写-->
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--加载映射文件-->
<mappers>
<mapper resource="com/huanf/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>
第六步: 在src/main/resources目录下新建com/huanf/mapper目录,里面新建UserMapper.xml文件,写入如下
<mapper namespace="com.huanf.mapper.UserMapper">
<!--查询。注意由于在sqlMapConfig写了'User类的全限定名'的别名为user,所以下面那行的parameterType属性值直接写user-->
<select id="findByCondition" parameterType="user" resultType="user">
select * from user where id=#{id} and username=#{username} and password=#{password}
</select>
</mapper>
第七步: 在src/test/java目录新建com.huanf.test.MapperTest类,写入如下
public class MapperTest {
public void test1() throws IOException {
//获得核心配置文件。注意写的是相对路径,也就是src/main/resources/sqlMapConfig.xml
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//使用getMapper获得mapper代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//模拟条件user,实际开发中这个user条件的数据是页面提供的,到web层进行封装的
User condition = new User();
condition.setId(1);
condition.setUsername("zhangsan");
condition.setPassword("123");
//获得数据。把模拟数据的condition对象放到下面那行。表示查询id为1,名字为zhangsan,密码为123对应的用户数据
List<User> userList = mapper.findByCondition(condition);
System.out.println(userList);
}
}
第八步: 在pom.xml写入如下
<dependencies>
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<!--引入MyBatis坐标-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--引入Junit坐标,用于测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--引入log4j,用于打印日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
第九步: 在MapperTest类运行测试
上面存在的问题: 当MapperTest类的模拟数据(用户前端填写的数据),如果没有用户没有填写password,那我们的程序就不能正确执行 分析: UserMapper.xml里面的查询语句不能连续两个and写死。解决: 在UserMapper.xml里面的查询语句,使用if标签来允许某些值可以为空
具体操作: 把原先的UserMapper.xml修改为如下
<mapper namespace="com.huanf.mapper.UserMapper">
<!--查询。注意由于在sqlMapConfig写了'User类的全限定名'的别名为user,所以下面那行的parameterType属性值直接写user-->
<select id="findByCondition" parameterType="user" resultType="user">
select * from user where 1=1
<!--当id不等于0,也就是id有值的时候-->
<if test="id!=0">
<!--才会把下面那行作为语句去执行-->
and id=#{id}
</if>
<!--同理-->
<if test="username!=null">
and username=#{username}
</if>
<!--同理-->
<if test="password!=null">
and password=#{password}
</if>
</select>
</mapper>
注意上面代码的1=1是额外加上去的,作用是避免出现where and的出现,因为SQL语句中where是不能直接接and
注意上面代码的'select * from user where 1=1'中的where 1=1,写的不规范,由于MyBatis已经为我们提供了一个
循环执行sql的拼接操作,例如: select * from user where id in(1,2,5),表示查询user表中id为1、2、5对应的数据
具体操作如下:
第一步: 把UserMapper接口修改为如下
public interface UserMapper {
//条件查询
public List<User> findByCondition(User user);
//in关键词查多个id对应的数据,范围查询
public List<User> findByIds(List<Integer> ids);
}
第二步: 在UserMapper.xml添加如下
xxxxxxxxxx
<!--范围查询。下面那行的list是别名,本来是写List,由于MyBatis为List起好了别名list,所以就用list-->
<select id="findByIds" parameterType="list" resultType="user">
<!--上面那行的user别名是我们在sqlMapConfig写的,这里可以直接用-->
<!--select * from user where id in(1,2,4)-->
<!--上面那行表示查询user表中id为1、2、4对应的数据,但是实际前端用户不一定是能传齐id为1、2、4,因此in括号里的是变化的。因此优化如下-->
select * from user
<where>
<!--用拼接where id in(1,2,4),例如collection是集合list还是数组array,open开括号,close闭括号,item每一项,separator分隔符-->
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
第三步: 在MapperTest类添加如下
public void test2() throws IOException {
//获得核心配置文件。注意写的是相对路径,也就是src/main/resources/sqlMapConfig.xml
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获得session工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//使用getMapper获得mapper代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//模拟封装id的集合
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(4);
//上面三个数据可以任意注释两个都不影响,模拟就是前端用户少传数据的情况
List<User> userList = mapper.findByIds(ids);
System.out.println(userList);
}
第四步: 在MapperTest类运行test2()测试
上面存在的问题: 在UserMapper.xml里写多条动态sql,所谓动态sql就是写了if标签的sql语句,所谓多条就是写了多个
为什么要抽取: 当后期我们的数据库表不叫user,而是叫other,如果你有很多
具体操作:
把UserMapper.xml修改为如下
<mapper namespace="com.huanf.mapper.UserMapper">
<!--sql语句的抽取,把抽取的作为片段-->
<sql id="selectUser">select * from user</sql>
<!--条件查询。注意由于在sqlMapConfig写了'User类的全限定名'的别名为user,所以下面那行的parameterType属性值直接写user-->
<select id="findByCondition" parameterType="user" resultType="user">
<!--上面那行的user别名是我们在sqlMapConfig写的,这里可以直接用-->
<!--select * from user-->
<!--上面那行被抽取出去了,我们只需要写<include>标签,然后refid引入被抽取的片段id即可,如下-->
<include refid="selectUser"></include>
<where>
<!--当id不等于0,也就是id有值的时候-->
<if test="id!=0">
<!--才会把下面那行作为语句去执行-->
and id=#{id}
</if>
<!--同理-->
<if test="username!=null">
and username=#{username}
</if>
<!--同理-->
<if test="password!=null">
and password=#{password}
</if>
</where>
</select>
<!--范围查询。下面那行的list是别名,本来是写List,由于MyBatis为List起好了别名list,所以就用list-->
<select id="findByIds" parameterType="list" resultType="user">
<!--上面那行的user别名是我们在sqlMapConfig写的,这里可以直接用-->
<!--select * from user where id in(1,2,4)-->
<!--上面那行表示查询user表中id为1、2、4对应的数据,但是实际前端用户不一定是能传齐id为1、2、4,因此in括号里的是变化的。因此优化如下-->
<include refid="selectUser"></include>
<where>
<!--用拼接where id in(1,2,4),例如collection是集合list还是数组array,open开括号,close闭括号,item每一项,separator分隔符-->
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
</mapper>