Mybatis
1、简介
1.1什么是Mybatis
- MyBatis 是一款优秀的持久层框架。
- 它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
如何获取Mybatis
-
maven库 :
-
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency>
-
Github : https://github.com/mybatis/mybatis-3/releases
-
中文文档 : https://mybatis.org/mybatis-3/zh/index.html
1.2、持久化
数据持久化
- 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
- 内存:断电即失
- 数据库(jdbc), io文件持久化(浪费资源)
为什么需要持久化
- 一些东东不能丢失
- 内存太贵
1.3、持久层
Dao层:持久层
Service层;业务操作
Controller层:仅接受用户的请求,并把请求转发给业务来做
。。。
- 完成持久化工作的代码块
- 层界限十分明显
1.4、为什么用Mybatis
- 帮助程序员将数据存入到数据库中去
- 方便
- 传统的JDBC代码复杂
- 不用Mybatis也可以,更容易上手,技术没有高低之分
最重要一点:使用的人多
2、第一个Mybatis程序
思路:搭建环境 --》 导入Mybatis --〉 编写代码 --》 测试
2.1、搭建环境
搭建数据库
新建项目
删除src目录
2.2、创建一个模块
-
编写mybatis的核心配置文件
<?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> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${com.mysql.jdbc.Driver"/> <property name="url" value="${jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8}"/> <property name="username" value="${root}"/> <property name="password" value="${********}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers> </configuration>
-
编写mybatis工具类
- 读取配置文件
2.3、编写代码
-
实体类
-
// 实体类 public class User { private int id; private String name; private String pwd; public User() { } public User(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
-
Dao接口
-
//Dao接口 package com.niu.dao; // Dao等价于Mapper import com.niu.pojo.User; import java.util.List; public interface UserDao { // 写一个方法 List<User> getUserList(); }
-
接口实现类
-
<?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"> <!--命名空间namespace = 绑定一个对应的Dao/Mapper接口--> <mapper namespace="com.niu.dao.UserDao"> <!-- <select id="selectBlog" resultType="Blog">--> <!-- select * from Blog where id = #{id}--> <!-- </select>--> <!--select查询语句--> <!--返回结果--> <select id="getUserList" resultType="com.niu.pojo.User"> select * from mybatis.user </select> </mapper> <!--接口实现类-->
2.4、测试
注意点:
报错:UserDao is not known to the MapperRegistry
每一个Mapper.xml 都需要在Mybatis核心配置文件中注册
xml <mappers> <mapper resource="com/niu/dao/UserMapper.xml"/> </mappers>
maven 由于他的 约定 大于 配置,所以我们写的配置文件可能无法被导出或者生效有问题,解决方案:
maven下的默认配置文件在resource下
<!--在build中配置resources, 来放置我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
- 配置文件没有注册 :每一个Mapper.xml 都需要在Mybatis核心配置文件中注册
- 接口绑定不对
- 出现【Caused by: org.apache.ibatis.exceptions.PersistenceException:】错误的时候,大多是自己的Mapper配置文件除了问题
- 出现这个org.apache.ibatis.exceptions.PersistenceException问题大多数都是找不到映射文件,这和没有遵循mybatis的mapper代理配置规范有关
- 安全验证问题:useSSL=false
- Maven导出资源问题 :大长串死代码
3、CRUD
3.1、命名空间
- namespace 中的包名要和Dao/mapper 接口的包名一致!
3.2、select
选择,查询语句;
- id:就是对应的namespace中的方法名(相当于重写了原来的方法)
- resultType:sql语句的执行的返回值!Class
- para
3.3、select
<!--select查询 语句--> <!--返回结果-->
<select id="getUserList" resultType="com.niu.pojo.User">
select *
from mybatis.user
</select>
<select id="getUserById" parameterType="int" resultType="com.niu.pojo.User">
select *
from mybatis.user
where id = #{id}
</select>
<!--对象的属性,可以直接取出来-->
<insert id="addUser" parameterType="com.niu.pojo.User">
insert into mybatis.user (`id`, `name`, `pwd`)
values (#{id}, #{name}, #{pwd});
</insert>
<!--参数类型="User"-->
<update id="updateUser" parameterType="com.niu.pojo.User">
update mybatis.user
set `name` = #{name},
`pwd` = #{pwd}
where `id` = #{id};
</update>
<update id="deleteUser" parameterType="com.niu.pojo.User">
delete
from mybatis.user
where `id` = #{id};
</update>
3.4、分析错误
- 标签不要匹配错
- resource绑定mapper,需要使用路径 “/” 连接
- 程序配置文件必须符合规范!
- NULLPointerException,没有注册到资源!
- 输出xml文件中存在中文乱码问题!
- maven资源没有导出!
3.5、万能的Map
假设,我们的实体类或者数据库中的表,字段或者参数过多,我们应该考虑考虑使用 Map
使用Map add 一个用户
int addUser2(Map<String,Object> map);
@Test
public void addUser2(){
// 定义sqlUserById
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("userid",5);
map.put("userName","Hello");
map.put("passWord","2222333");
mapper.addUser2(map);
sqlSession.close();
}
<!--万能的Map
传递Map的 key!-->
<!--map可以随意制造参数,sql想怎么写就怎么写,但对象的话,就得把参数(有几个)都得写出来-->
<insert id="addUser" parameterType="map">
insert into mybatis.user (`id`, `pwd`)
values (#{userid}, #{passWord}, #{ });
</insert>
- Map传递参数,直接在sql中取出key即可 [parameterType = “map”]
- 对象传递参数,直接在sql中取对象的属性即可 [parameterType = “Object”]
- 只有一个基本类型参数的情况下,可以直接在sql中取到!
- 多个参数使用Map, 或者注解
3.6、思考题
模糊查询怎么写
- Java代码执行的时候,传递通配符 % %
List<User> userList = mapper.getUserLike("%李%");
- 在sql拼接中使用通配符
select * from mybatis.user where name like "%"#{value}"%"
4、配置解析
4.1、核心配置文件
- mybatis-config.xml
- Mybatis 的配置文件包含了会深深影响Mybatis行为的设置和属性信息
configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器)
4.2、环境配置(environments)
Mybatis可以配置成适应多种环境
不过要记住,尽管可以配置多个环境,但每一个SqlSessionFactory 实例只能选择一中环境
学会使用配置多套运行环境: (多个id)
Mybatis默认的事务管理器JDBC, 连接池: POOLED
4.3、属性(properties)
我们可以通过properties属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
编写一个配置文件 db.properties
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybatis?
useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=************
在核心配置文件中引入
<!-- properties 必须写到最上面(有顺序要求)-->
<!--引入外部配置文件(自闭合了)-->
<!-- <properties resource="db.properties"/>-->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="pwd" value="11111"/>
<!--发现依旧成功,说明优先使用外部配置文件-->
</properties>
- 可以直接引入外部文件
- 可以在其中增加一些属性配置
- 如果两个文件有同一个字段,优先使用外部配置文件的
4.4、别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。
它仅用于 XML 配置,意在降低冗余的全限定类名书写。
-
可以这样子:
<!--可以给实体类起别名--> <typeAliases> <typeAlias type="com.niu.pojo.User" alias="User" /> </typeAliases>
-
也可以指定一个包名,MyBatis会在包名下面搜索需要的Java Bean,比如:
-
扫描实体类的包,它的默认别名就为这个类的类名,首字母小写!
<typeAliases> <package name="com.kuang.pojo"/> </typeAliases>
注意:
1. 在实体类比较少的时候,使用第一种方式
2. 实体类比较多时候,建议第二种】
3. 第一种可以DIY别名,第二种需要这样增加注解
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
4.5、映射器(mappers)
<mappers>
<mapper resource="com/niu/dao/UserMapper.xml"/>引入XML文件
</mappers>
<mappers>
<mapper class="com.niu.dao.UserMapper"/>
</mappers>
注意:
- 接口和他的Mapper配置文件必须同名
- 接口和他的Mapper配置文件必须同一个包下
总
- 数据库配置文件外部引入
- 实体类别名
- 保证UserMapper接口和UserMapper.xml的改为一致,并在同一个包下
5、日志
5.1log4j
什么是Log4j
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;
- 我们也可以控制每一条日志的输出格式;
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
- 引入包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 配置文件
# 将等级为DEBUG的日志信息输出到console和file这养个目的地,console和file的定义在下面的代码
log4j.rootLogger = DEBUG,console,file
# 控制台输出相关的设置
1og4j.appender.console=org.apache.1og4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
1og4j.appender.console.layout=org.apache.log4j.PatternLayout
1og4j.appender.console.layout.ConversionPattern=[%c]-%m%n
# 文件输出的相关设置
1og4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/niu.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
1og4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
# 日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
- 配置log4j为日志的实现
<settings>
<setting name="logImp1" value=""/>
</settings>
简单实用
-
在要使用Log4j的类中,导入包。
import org.apache.log4j.Logger; -
日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(UserDaoTest.class); -
日志级别
Logger.info("info:进入了testLog4j");
Logger.debug("debug:进入了testLog4j");
Logger.error("error:进入了testLog4j");
6、分页
6.1、使用 Limit 分页
SELECT * FROM `user` LIMIT startIndex.pageSize;
SELECT * FROM `user` LIMIT 3; # [0,n]
使用 Mybatis 实现分页核心 SQL
- 接口
List<User> getUserByLimit(Map<String,Integer> map);
- Mapper.xml
<!--分页!--> <!--指向结果集映射-->
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
</select>
- 测试
// 分页!
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<String,Integer>();
map.put("startIndex", 0);
map.put("pageSize", 2);
List<User> userList = mapper.getUserByLimit(map);
for (User user:userList){
System.out.println(user);
}
sqlSession.close();
}
6.2、使用 RowBounds 分页
- 接口
// 分页2
List<User> getUserByRowBounds();
- mapper.xml
<!--分页2!--> <!--指向结果集映射-->
<select id="getUserByRowBounds" resultMap="UserMap">
select * from mybatis.user
</select>
- 测试
void getUserByRowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//RowBounds实现 从第一个开始,一共两条数据
RowBounds rowBounds = new RowBounds(1, 2);
// 通过java代码层实现分页
List<User> userList = sqlSession.selectList("com.niu.dao.UserMapper.getUserByRowRounds",null, rowBounds);
for (User user:userList){
System.out.println(user);
}
sqlSession.close();
}
6.3、分页 插件
7、一些报错
Type handler was null on parameter mapping for property ‘createTime‘.】
Type handler was null on parameter mapping for property ‘createTime’. It was either not specified and/or could not be found for the javaType (javax.xml.crypto.Data) : jdbcType (null) combination.
Error updating database. Cause: java.lang.IllegalStateException: Type handler was null on parameter mapping for property ‘createTime’. It was either not specified and/or could not be found for the javaType (lombok.Data) : jdbcType (null) combination.
Cause: java.lang.IllegalStateException: Type handler was null on parameter mapping for property ‘createTime’. It was either not specified and/or could not be found for the javaType (lombok.Data) : jdbcType (null) combination.
原因是:在创建实体类的时候吧date类型写成data 导致类型不匹配
此处总结:在写实体类时要注意一些类型的导包,不要导错
再尝试Mybatis 自动注入时间的时候出现的错误
解决 : 时间类型错了。 不可以使用 sql.Date 得使用 until.Date