一、前言
本文需要导入的坐标:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
紧接着上一篇文章,我们使用上一篇文章的pojo和接口类:
public class User {
private int id;
private String username;
private String password;
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
public interface UserMapper {
public List<User> findByCondition(User user);
public List<User> findByIds(List<Integer> ids);
}
核心配置文件也不变:
<configuration>
<!--通过properties标签加载外部properties文件-->
<properties resource="jdbc.properties"/>
<!--取别名-->
<typeAliases>
<typeAlias type="com.yds.domain.User" alias="user"/>
</typeAliases>
<!--配置数据源环境-->
<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>
<!--加载映射文件-->
<mappers>
<mapper resource="com.yds.mapper/UserMapper.xml"/>
</mappers>
</configuration>
接下来我们将重写映射文件。
二、动态sql
1.if标签
我们首先先不使用动态sql,来看看传统sql的缺点:
我们首先查询特定id、特定username和特定密码的user:
<!--动态sql-if-->
<select id="findByCondition" parameterType="user" resultType="user">
select * from user where id=#{id} and username=#{username} and password=#{password}
</select>
编写测试类:
public class MapperTest {
@Test
public void test1() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//模拟条件user
User condition = new User();
condition.setId(1);
condition.setUsername("zhangsan");
condition.setPassword("123");
//模拟findByCondition
List<User> userList = mapper.findByCondition(condition);
System.out.println(userList);
}
}
执行后发现是可以正常查询的:

但是如果我们只想查询特定id和username的user(password无所谓)的时候,我们会发现有问题了:
这里把password注掉:
public class MapperTest {
@Test
public void test1() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//模拟条件user
User condition = new User();
condition.setId(1);
condition.setUsername("zhangsan");
//condition.setPassword("123");
//模拟findAll
List<User> userList = mapper.findByCondition(condition);
System.out.println(userList);
}
}
再次执行,发现竟然是空的:

这个时候就有问题了,明明我放宽了查询的限制条件,怎么还反而查不到了呢?
原因在于,我们的sql语句使用了and作为连接,and表示需要同时满足几个需求,这里我们传统的sql语句的弊端就出来了,我们放宽了限制条件时,意味着我们需要在sql语句中去掉一个and,否则我们将查询不到,可是这样会导致一个问题,就是我们会因为不同的查询条件写出很多种sql语句,才能达到效果,于是我们为了解决这个问题,使用if标签来解决了。
<!--动态sql-if-->
<select id="findByCondition" parameterType="user" resultType="user">
select * from user
<where>
<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>
可以看到,这里我们设置了条件,当查询时对username或者password有限制时,我们才会在sql语句后面拼接一个and,如果没有条件,将直接跳过if标签,意味着当我们放宽查询条件时,sql语句也会相应放宽条件(不额外添加查询条件限制以外的and)
依旧放宽对password查询的限制,显然的,这里的password将为null,在执行到if标签时,将直接跳过,所以语句将变为: select * from user where id=#{id} and username=#{username}
效果如下:

2.foreach标签
顾名思义,这是一个遍历标签,假设我们需要查询几个id的user,我们就不需要一个一个去写sql语句了,我们使用foreach标签就可以直接解决:
注意:这里我们的参数是一个列表,因为我们需要将想要查询的id存到这个列表中,后续遍历这个列表,我们就能查询到相应id的数据了,foreach标签的格式如下:
<select id="findByIds" parameterType="list" resultType="user">
select * from user
<where>
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
这里我们尝试查询id为1和2的user数据,所以我们向list中添加两个id值,然后使用mapper接收结果集,最后打印出来:
public class MapperTest {
@Test
public void test1() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//模拟ids的数据
List<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
List<User> userList = mapper.findByIds(ids);
System.out.println(userList);
}
}
效果如下:

3.sql语句抽取
为了简化相同代码的书写,我们将使用频率高的sql语句抽取出来:
<!--sql语句的抽取-->
<sql id="selectUser">
select * from user
</sql>
所以我们的查询就可以改为(使用抽取语句时用include标签):
<!--动态sql-if-->
<select id="findByCondition" parameterType="user" resultType="user">
<include refid="selectUser"/> /*sql语句的抽取*/
<where>
<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>
结果自然相同:

三、类型转换
当我们想向数据库中存入非基本类型时,我们往往需要将自定义类型 转换为基本类型,比如存入一个Date类型的数据,我们并不能直接将它存入数据库,而是需要转换为毫秒值存入,当然,当我们查询时,我们也不希望看到毫秒值,所以我们需要又将毫秒值转换为Date类型,而进行转换的类我们就称作typeHandler。
我们首先测试一下,在没有typeHandler的情况下存入Date数据到表中会发生什么:
//测试数据库插入自定义类型转换
@Test
public void test1() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//创建user
User user = new User();
user.setUsername("try");
user.setPassword("abc");
user.setBirthday(new Date());
//执行保存操作
mapper.save(user);
sqlSession.commit();
sqlSession.close();
}
public interface UserMapper {
public void save(User user);
public User findById(int id);
public List<User> findAll();
}
<insert id="save" parameterType="user">
insert into user values (#{id},#{username},#{password},#{birthday})
</insert>
显然的,肯定是存不进去的:

于是我们创建一个类型转换处理器类(继承BaseTypeHandler<需要转换的类型>):
public class DateTypeHandler extends BaseTypeHandler<Date> {
//将Java类型 转换为数据库需要的类型
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
long time = date.getTime();
preparedStatement.setLong(i,time);
}
//将数据库中类型转换成Java类型
//String参数 表的字段的名称
//ResultSet 查询结果集
@Override
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
//获得结果集中需要的数据(long)转换为Date类型 返回
long aLong = resultSet.getLong(s);
Date date = new Date(aLong);
return date;
}
//将数据库中类型转换成Java类型
@Override
public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
long aLong = resultSet.getLong(i);
Date date = new Date(aLong);
return date;
}
//将数据库中类型转换成Java类型
@Override
public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
long aLong = callableStatement.getLong(i);
Date date = new Date(aLong);
return date;
}
}
同时需要注册类型转换处理器
<!--自定义注册类型处理器-->
<typeHandlers>
<typeHandler handler="com.yds.handler.DateTypeHandler"/>
</typeHandlers>
查看日志,已经将毫秒值存入了:

表中也添加成功了:

那么再测试将毫秒值用Date类型拿出来:
//测试查询自定义类型转换结果
@Test
public void test2() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findById(7);
System.out.println("user中的birthday: " + user.getBirthday());
sqlSession.commit();
sqlSession.close();
}
<select id="findById" parameterType="int" resultType="user">
select * from user where id=#{id}
</select>
成功拿取:

四、分页插件
首先需要导入坐标:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
然后在核心配置文件中配置插件:
<!--配置分页助手插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
直接在测试类中用分页插件分页,并且获取分页信息(这里设置查看第二页,每页三条):
<select id="findAll" resultType="user">
select * from user
</select>
//分页测试
@Test
public void test3() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//设置分页参数 当前页和每页显示的条数
PageHelper.startPage(2, 3);
List<User> userList = mapper.findAll();
for (User user : userList) {
System.out.println(user);
}
//获得与分页相关的参数
PageInfo<User> pageInfo = new PageInfo<>(userList);
System.out.println("当前页:"+pageInfo.getPageNum());
System.out.println("每页显示条数:"+pageInfo.getPageSize());
System.out.println("总条数:"+pageInfo.getTotal());
System.out.println("总页数:"+pageInfo.getPages());
System.out.println("上一页:"+pageInfo.getPrePage());
System.out.println("下一页:"+pageInfo.getNextPage());
System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
sqlSession.commit();
sqlSession.close();
}
第二页就被展示出来了(包括信息):


1378

被折叠的 条评论
为什么被折叠?



