之前在a_28_0学了核心配置文件的简单配置,这节课将深入学习MyBatis的核心配置文件 学这节课之前,请你保证已经对上节课映射文件的学习已经掌握,并且上节课的环境你是已经通过测试的,这节课将继续使用上节课的环境 请你保证已经对上节课dao层的代理开发方式很熟悉,因为这节课我们写接口不写对应的实现类,也就是使用代理开发方式简化掉实现类 并且还要熟悉前面a_27学的增删改查
无论是MyBatis在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成Java类型。 简单说就是java中的数据类型,跟数据库中的数据类型是不同的,两者其实是会经过中间者,将这两者的数据类型互相转换,中间者就是类型处理器 下表描述了一些默认的类型处理器
类型处理器 | Java类型 | JDBC类型 |
---|---|---|
BooleanTypeHandler | java.lang.Boolean,boolean | 数据库兼容的BOOLEAN |
ByteTypeHandler | java.lang.Byte,byte | 数据库兼容的NUMERIC或BYTE |
ShortTypeHandler | java.lang.Short,short | 数据库兼容的NUMERIC或SHORT INTEGER |
IntegerTypeHandler | java.lang.Integer,int | 数据库兼容的NUMERIC或INTEGER |
LongTypeHandler | java.lang.long,long | 数据库兼容的NUMERIC或LONG INTEGER |
MyBatis提供的类型处理器是有限的,如果无法满足我们我们要将java中的某种类型解析转换成JDBC类型时,此时就需要我们自定义类型处理器。 你可以重写类型处理器或创建你自己的类型处理器,来解决MyBatis提供的类型处理器无法转换的指定类型。具体做法如下 具体做法: 实现org.apache.ibatis.type.TypeHandler接口,或继承一个很便利的类org.apache.ibatis.type.BaseTypeHandler,然后可以选择性地将java类型映射到JDBC类型上
例如需求: 一个Java中的Data数据类型,如何将该数据存到数据库的时候,在数据库存成一个1970年至今的毫秒数,从数据库取出来时就转换为java的Date类型。简单来说 就是java的Date数据类型,与数据库vachar数据类型的毫秒值之间转换
需求实现步骤:
1、定义转换类继承类BaseTypeHandler
2、覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时mysql的字符串类型转换成java中的Type类型的方法
3、在MyBatis核心配置文件中进行注册
4、测试转换是否正确
该需求的难点: 默认的类型处理器无法处理日期对象转换成JDBC类型的长整型,只能将java的Date类型转换为JDBC的Date类型 该需求的解决: 需要自定义类型处理器,将时间转换成毫秒值,然后再插入到数据库中 这个需求会在下面具体演示,首先就是搭建环境,如下
具体操作如下
第一步: 确保数据库的test库里面有user表,并且表内是如下数据
create table user(
id int primary key auto_increment comment '主键',
username varchar(10) comment '姓名',
password varchar(20) comment '密码',
birthday bigint comment '生日'
) comment '用户表';
insert into user values (null,'zhangsan','123',null),(null,'lisi','123',null),(null,'wangwu','123',null),(null,'zhaoliu','123',null),(null,'tianqi','123',null);
第二步: 在src/main/java目录下,右键java,新建com.huanf.domain.User类,写入如下
//数据库中的user表数据
public class User {
//对应表中的字段
private int id;
private String username;
private String password;
//用于数据转换
private Date birthday;
//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 Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
第三步: 在src/main/java目录下,右键java,新建com.huanf.mapper.UserMapper接口,写入如下
public interface UserMapper {
public void save(User user);
}
第四步: 在src/main/resources目录下新建jdbc.properties文件,写入如下
xxxxxxxxxx
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文件,写入如下
xxxxxxxxxx
<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文件,写入如下
xxxxxxxxxx
<mapper namespace="com.huanf.mapper.UserMapper">
<insert id="save" parameterType="user">
insert into user values(#{id},#{username},#{password},#{birthday})
</insert>
</mapper>
第八步: 在src/test/java目录新建com.huanf.test.MapperTest类,写入如下
public class MybatisTest {
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 user = new User();
user.setUsername("测试");
user.setPassword("abc");
user.setBirthday(new Date());//日期传的是当前时间
//执行UserMapper接口的save方法
mapper.save(user);
//提交事务
sqlSession.commit();
sqlSession.close();
}
}
第九步: 在pom.xml写入如下
xxxxxxxxxx
<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类运行测试,由于我们搭建的是测试环境,所以此时运行是报错的
原因: 默认的类型处理器无法处理日期对象转换成JDBC类型的长整型,只能将java的Date类型转换为JDBC的Date类型 下面会进行解决: 需要自定义类型处理器,将时间转换成毫秒值,然后再插入到数据库中
需求实现步骤:
1、定义转换类继承类BaseTypeHandler
2、覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时mysql的字符串类型转换成java中的Type类型的方法
3、在MyBatis核心配置文件中进行注册
4、测试转换是否正确
具体操作:
第一步: 在src/main/java/com.huanf目录下新建handler.DateTypeHandler类,写入如下
//自定义Date类型的处理器
public class DateTypeHandler extends BaseTypeHandler<Date> {
//将java类型转换成数据库需要的类型,例如数据库需要的是长整型的数据,那么就使用setLong方法
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
long time = date.getTime();//获得时间的毫秒值
preparedStatement.setLong(i,time);
}
//将数据库中的类型转换成java类型,其中的String表示要转换的数据库字段名称,resultSet表示数据库中查询出的结果集
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
long aLong = resultSet.getLong(s);//获得'结果集'中需要的数据(该数据的类型是Long)
Date date = new Date(aLong);//将获取的结果集数据转换成Date类型
return date;
}
//将数据库中的类型转换成java类型,其中的i表示字段的位置
public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
long aLong = resultSet.getLong(i);//获得'结果集'中需要的数据(该数据的类型是Long)
Date date = new Date(aLong);//将获取的结果集数据转换成Date类型
return date;
}
//将数据库中的类型转换成java类型,
public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
long aLong = callableStatement.getLong(i);//获得'结果集'中需要的数据(该数据的类型是Long)
Date date = new Date(aLong);//将获取的结果集数据转换成Date类型
return date;
}
}
第二步: 在sqlMapConfig里面添加如下
<!--注册'自定义类型处理器'-->
<typeHandlers>
<typeHandler handler="com.huanf.handler.DateTypeHandler"></typeHandler>
</typeHandlers>
第三步: 在MyBatisTest类测试test1,然后去数据库查看数据是否插入
第一步: 在UserMapper.xml里面添加如下
xxxxxxxxxx
<!--根据name查询数据库中的数据-->
<select id="findByName" parameterType="string" resultType="user">
select * from user where username=#{username}
</select>
第二步: 在UserMapper接口添加如下
xxxxxxxxxx
//查询数据库中的数据
public User findByName(String name);
第三步: 在MyBatisTest类添加如下,并运行测试test2
//查询把数据库中的毫秒值,要求查到的是具体的时间,而不是毫秒值
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);
User user = mapper.findByName("测试"); //查询'测试'名字的生日时间
System.out.println("user中的birthday: "+user.getBirthday());
//提交事务
sqlSession.commit();
sqlSession.close();
}
MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据
开发步骤:
1、导入通用PageHelper的坐标
2、在mybatis核心配置文件中配置PageHelper插件
3、测试分页数据获取
具体步骤:
第一步: 在pom.xml添加如下
<!--导入plugin分页查询的坐标,分页助手插件的坐标-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<!--导入jsql解析器,用于分页查询-->
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
第二步: 在UserMapper接口添加如下
//查询数据库中的全部数据
public List<User> findAll();
第三步: 在sqlMapConfig.xml添加如下
xxxxxxxxxx
<!--配置分页助手插件-->
<plugins>
<!--我们在pom.xml使用的是3.7.5版本,如果使用的分页助手插件是5.x.x版本的,就把PageHelper改为PageInterceptor,并且dialect那行不用配置-->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!--dialect表示方言,作用是不同的数据库可能使用的SQL结构化查询语言可能是不同的,所以需要配置方言指令-->
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
第四步: 在UserMapper.xml添加如下
<!--查询数据库中的全部数据-->
<select id="findAll" resultType="user">
select * from user
</select>
第五步: 在MyBatisTest类添加如下,并执行测试test3
//查询数据库中的全部数据,需要使用分页助手插件
public void test3() 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);
//设置分页的相关参数,也就是当前页+每页显示的条数,使得查询出来的数据在控制台是分页形式输出
PageHelper.startPage(1,3);//展示的当前页是第一页,每页展示3条数据。要查第2页的话,把1改成2即可
List<User> userList = mapper.findAll();
//查询全部并遍历,遍历的作用是配合分页查询
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
上面的分页查询虽然实现了功能,但是还存在问题: 如何根据当前查询出来的数据确定当前也是第几页,当前页的上下页是谁,是否是最后一页。这些参数都是还没实现的 其实实现很简单,只需要改测试代码即可
具体操作:
在MyBatisTest类把test3()修改为如下,并执行测试test3()
//查询数据库中的全部数据,需要使用分页助手插件
public void test3() 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);
//设置分页的相关参数,也就是当前页+每页显示的条数,使得查询出来的数据在控制台是分页形式输出
PageHelper.startPage(2,3);//展示的当前页是第2页,每页展示3条数据。要查第1页的话,把2改成1即可
List<User> userList = mapper.findAll();
//查询全部并遍历,遍历的作用是配合分页查询
System.out.println("------------------------------------------------------------------------");
for (User user : userList) {
System.out.println(user);
}
System.out.println("------------------------------------------------------------------------");
//获得与分页相关的参数,例如当前页、上一页、下一页。泛型写User是因为我们数据是从User类拿的,userList是分页查询出来的数据
PageInfo<User> pageInfo = new PageInfo<User>(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("是否是第1页: "+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页: "+pageInfo.isIsLastPage());
sqlSession.close();
}