目录
1、Mybatis概述
1.1、ORM概述
1.2、常见的ORM框架
1.3、Mybatis优缺点
1.4、Mybatis的工作原理
2、Mybatis快速入门
2.1、数据库准备
2.2、导入依赖
2.3、编写全局配置文件
2.4、jdbc.properties配置文件
2.5、pojo实体类
2.6、实现步骤
2.7、测试
2.8、添加日志
3、Mybatis实现CRUD
3.1、创建dao层
3.2、编写sql映射文件UserMapper.xml
3.3、加载sql映射文件
3.4、编写测试类
4、解决数据库字段名和实体类属性名不一致的问题
4.1、sql语句起别名
4.2、全局配置开启驼峰匹配
5、优化接口测试类
5.1、删除UserDaoImpl类
5.2、修改UserMapper.xml文件中的namespace
5.3、修改包名和接口名
5.4、修改测试类
6、mybatis-config.xml配置
6.1、typeAliaseso类别名
6.2、environments环境
6.3、mappers映射器
7、mapper.xml映射文件
7.1、select标签
7.2、insert标签
7.3、update标签
7.4、delete标签
在使用JDBC 的时候,我们通常将数据直接返回,但现在也会将数据封装到实体类对象中,由对象携带数据。这样操作的时候,可以通过操作对象的方式操作数据。但是手写这类代码通常是繁琐的、重复的,如果有自动完成这些功能的程序就好了。
ORM
(Object-Relational-Mapping):是对象关系映射的意思,它是一种思想,是指将数据库中的每一行数据用对象的形式表现出来。可以将 ORM 简单理解为上面我们提出的,可以自动将对象与数据进行映射的技术。
Hibernate: 完全ORM映射框架
优点:
1、简化了整个jdbc操作过程
2、对于开发者来说不需要关心sql了,只需要去操作对象就可以了,hibernate可以帮我们自动生成所需要的sql
3、代码移植性比较好,通过hibernate操作db都是通过操作对象来进行的,而hibernate会根据我们的操作和db的类型生成符合各种db要求的sql,如果我们需要切换db的类型,hibernate会自动适应,对于开发者业务代码来说不需要做任何业务代码上的调整
4、开发效率比较高
缺点:
1、sql优化比较艰难,各种操作最终发给db的sql是由hibernate自动生成的,对于开发者来说如果想干预最终需要执行的sql,相对来说比较困难
2、hibernate入门比较容易,但是想成为高手学习成本比较高
3、对于复杂的动态sql,代码中也需要写很多判断进行组装,动态sql这块的支持比较欠缺
如果做一些简单的系统,开发周期也比较紧急,对sql的优化要求也不是很高,可以使用hibernate。
JdbcTemplate :半自动的ORM框架
jdbcTemplate是在Spring框架的基础上开发的一个jdbc框架,所以对spring是有依赖的,它对jdbc做了封装,隐藏了各种重复的操作,使用时只需传入:需要执行的sql、参数以及对于结果如何解析的程序就可以了,使用起来还是很方便的,但是面对与动态sql,它也是无能为力了。整体上来说,jdbctemplate相对于纯jdbc隐藏了很多重复性的操作,对于sql的写法和结果的组装上完全交给了开发者自己去控制,在系统中使用也可以帮助我们节约很多时间,而且学习相当简单.
MyBatis:半自动的化的ORM框架
MyBatis是一个半自动化的orm框架,为什么说是半自动化的呢,因为他需要我们自己去写sql,而他做的更好的地方就是动态sql的支持上面,而上面说的各种技术,面对与动态sql只能自己写很多判断去组装sql,而这些判断和组装在mybatis中实现起来就非常简单了,完全由mybatis去帮我们实现了。mybatis将sql交由开发者去控制,所以在sql的优化方面,开发者可以随心所欲,也就是说mybatis将重复性的工作优化到了极致:操作db的过程、动态sql的拼装、结果和对象的映射,这些mybatis都帮我们实现的很好,而让我们将更多的经历花在sql的写法和优化上面,所以毫无疑问mybatis使用人数和公司也是最多的。
MyBatis官网地址:mybatis – MyBatis 3 | 简介
1、mybatis是一个半自动化的持久层框架。
2、mybatis封装了jdbc的很多细节,开发者只需要关注sql本身,无需关注注册驱动、获取连接等操作。
3、mybatis使用ORM思想来对结果集一个封装。
优点:
1、简单易学,容易上手(相比于 Hibernate)基于SQL编程。
2、消除了JDBC大量冗余的代码,不需要手动开关连接。
3、很好的与各种数据库兼容(因为 MyBatis 使用JDBC来连接数据库,所以只要JDBC 支持的数据库 MyBatis 都支持,而JDB提 供了可扩展性,所以只要这个数据库有针对Java的jar包就可以就可以与 MyBatis 兼容),开发人员不需要考虑数据库的差异性。
4、提供了很多第三方插件(分页插件 / 逆向工程)。5、能够与Spring很好的集成。
6、如果使用映射文件的话,可以让代码和配置文件完全分离。只要方法的定义没有改变,那么只需要修改配置文件就可以达到修改的目的。缺点:
SQL语句的编写工作量较大,尤其是字段多、关联表多时,更是如此,对开发人员编写SQL语句的功底有一定要求。
SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
Mybatis的工作原理如上图,需要首先创建一个Mybatis的全局配置文件mybatis-config.xml(名称不固定,可以随便起),其次是多个mapper.xml配置文件用来书写sql语句,为了能够使用这些配置文件,我们需要通过SqlSessionFactory创建一个SqlSession对象(这个对象是Mybatis的核心对象,CRUD都是通过该对象进行调用),后通过executor底层执行器执行sql语句,得到结果集返回。
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) DEFAULT NULL,
`password` varchar(20) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`sex` char(1) DEFAULT NULL,
`email` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;
自行添加数据,这是我的数据
此次演示使用的是Maven工程,所以需要导入Mybatis的坐标依赖,以及mysql数据库连接依赖,还有一个就是lombok依赖,用来使用注解生成实体类的set/get方法。
org.mybatis
mybatis
3.5.6
mysql
mysql-connector-java
5.1.18
org.projectlombok
lombok
1.16.18
provided
org.junit.jupiter
junit-jupiter-api
RELEASE
compile
junit
junit
4.11
mybatis-config.xml是Mybatis的全局配置文件,也是Mybatis里很重要的配置文件
注意事项:properties标签是引入jdbc.properties数据库连接配置文件的,environments标签是配置环境,里面就是获取mysql的连接配置,固定模版,直接粘贴即可。mapper标签是用来获取映射文件的路径。
创建jdbc.properties配置文件放到resources包下
driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///数据库名
username=root
password=你的数据库密码
切记一定要把所有的配置文件放到resources包下,我是为了方便整理,所以创建了一个mapper包,根据个人喜好,也可不用创建,注意:如果创建了mapper包,在mapper映射路径中切记要加上mapper
@Data
public class User {
private int id;
private String username;
private String password;
private int age;
private String sex;
private String email;
}
创建一个测试类
public class MybatisDemo{
public static void main(String[] args) throws IOException {
//获取核心配置文件
String resource="mybatis-config.xml";
//加载配置文件
InputStream inputStream= Resources.getResourceAsStream(resource);
//构建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//构建Sqlsession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行sql 第一个参数为xml文件中namespace名称,第二个参数就是参数值id
User user = sqlSession.selectOne("UserMapper.selectById",8);
System.out.println(user);
}
}
创建对应的sql映射配置文件selectUserMapper.xml
把之前的mybatis-config.xml全局配置文件中最后一行的mapper标签的路径填写为sql映射文件的路径(切记不要忘了)
运行测试类,得到了sql语句查询的结果
然而这种直接得出结果的方式并不友好,不方便看到运行的过程,所以我们需要导入log4j坐标,可以在运行的时候查看到运行日志,方便调试程序。
org.slf4j
slf4j-log4j12
1.6.4
在resources包下创建log4j.properties配置文件
固定模版,直接粘贴复制即可
log4j.rootLogger=DEBUG,A1
log4j.logger.org.mybatis=DEBUG
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
再次运行测试类
可以看到这次就能看到具体的sql语句,以及传递的参数,还有返回结果的个数
public interface UserDao {
/**
* @description:根据id查询用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 14:45
* @param: []
* @return: cn.itssl.pojo.User
**/
User queryUserById(int id);
/**
* @description:查询所有用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 14:45
* @param: []
* @return: java.util.List
**/
List queryUserAll();
/**
* @description:删除用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 14:46
* @param: []
* @return: void
**/
void deleteUserById(int id);
/**
* @description:添加用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 14:46
* @param:
* @return:
**/
void addUser(User user);
/**
* @description:根据id修改用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 14:47
* @param: [id]
* @return: void
**/
void updateUser(User user);
}
public class UserDaoImpl implements UserDao {
private SqlSession sqlSession;
//通过有参构造获取sqlSession对象
public UserDaoImpl(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
/**
* @description:根据id查询用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 15:37
* @param: [id]
* @return: cn.itssl.pojo.User
**/
public User queryUserById(int id) {
return sqlSession.selectOne("UserMapper.queryUserById", 8);
}
/**
* @description:查询所有用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 15:37
* @param: []
* @return: java.util.List
**/
public List queryUserAll() {
return sqlSession.selectList("UserMapper.queryAllUser");
}
/**
* @description:根据id删除用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 15:37
* @param: [id]
* @return: void
**/
public void deleteUserById(int id) {
sqlSession.delete("UserMapper.deleteUser",id);
}
/**
* @description:添加用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 15:43
* @param: [user]
* @return: void
**/
public void addUser(User user) {
sqlSession.insert("UserMapper.addUser", user);
}
/**
* @description:根据id删除用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 15:43
* @param: [id]
* @return: void
**/
public void updateUser(User user) {
sqlSession.update("UserMapper.updateUser",user);
}
}
delete
from t_user
where id = #{id}
insert into t_user
values (null, #{username}, #{password}, #{age}, #{sex}, #{email})
update t_user
set username=#{username},
password=#{password},
age=#{age},
sex=#{sex},
email=#{email} where id=#{id};
public class MybatisDemo2 {
private UserDao userDao;
private SqlSession sqlSession;
//在执行test测试之前一定会执行Before
@BeforeEach
public void setUp() throws IOException {
//获取核心配置文件
String resource = "mybatis-config.xml";
//加载配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
//构建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//构建Sqlsession对象 这里传递一个true为了使数据库开启自动事务
sqlSession = sqlSessionFactory.openSession(true);
//把sqlSession对象传递至dao层
userDao = new UserDaoImpl(sqlSession);
}
//根据id查询
@Test
public void queryUserById() {
User user = this.userDao.queryUserById(8);
System.out.println(user);
}
//查询所有
@Test
public void queryAllUser(){
List users = this.userDao.queryUserAll();
for (User user : users) {
System.out.println(user);
}
}
//删除用户
@Test
public void deleteUserById(){
this.userDao.deleteUserById(7);
}
//添加用户
@Test
public void addUser(){
User user=new User();
user.setUsername("刘备");
user.setPassword("1234");
user.setAge(23);
user.setSex("男");
user.setEmail("[email protected]");
this.userDao.addUser(user);
}
//修改用户
@Test
public void updateUser(){
User user=new User();
user.setId(10);
user.setUsername("张飞");
user.setPassword("147852");
user.setAge(22);
user.setSex("男");
user.setEmail("[email protected]");
this.userDao.updateUser(user);
}
}
可能在以后的开发中存在这样的问题,就是数据库在创建字段时候按照数据库的命名规则用户名命名为user_name,而在Java实体类中变量值按照大驼峰命名为userName,这样就会造成一种查询不到用户名的问题,因为名字不一致。
就是在查询语句中把user_name起别名,这样就可以获取到查询的用户名,不过这种方式太过麻烦,如果查询语句上千条,那岂不是一条一条的添加(太麻烦,不推荐)
Select * , user_name as username from t_user;
最快速有效的方式,Mybatis为我们提供了一个驼峰匹配的属性,所以需要我们在全局配置文件中进行手动配置,开启驼峰匹配
在mybatis-config.xml文件中加入下面代码
注意:一定要按照xml文件的定义顺序进行书写,否则出现异常,按照异常提示,从左向右是标签定义的先后顺序。
我们是否有这样的一个问题,就是在dao的实现类还要通过调用sqlsession的方法,然而真正实现CRUD功能却是在sql映射文件中,这样不是感觉多此一举吗,所以,为了方便我们开发,需要把实现类进行删除,直接由接口对接UserMapper.xml的sql映射文件。
在之前有实现类的时候,namespace我们是随便起的名字,然而现在就不能随便起了,需要填写UserDao接口的路径,因为只有这样才能把userDao接口与UserMapper.xml关联起来,之前是通过实现类里调用sqlsession的方法来关联的。
修改前:
为了方便后面的理解,我们以后就不能使用dao来做接口的名称了,就用mapper来替代。记得UserMapper里面的路径也改为mapper,修改类名时应该会自动替换,如果没有则进行手动修改。
可以看到,当删除实现类后,UserDaoImpl的有参构造就报错了,我们就不能使用该方法了,那么就使用动态代理来获取UserDao。
修改后的测试类:
public class MybatisDemo2 {
private UserMapper userMapper;
private SqlSession sqlSession;
@Before
public void setUp() throws IOException {
//获取核心配置文件
String resource = "mybatis-config.xml";
//加载配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
//构建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//构建Sqlsession对象
sqlSession = sqlSessionFactory.openSession(true);
//把sqlSession对象传递至dao层
// userDao = new UserDaoImpl(sqlSession);
//使用动态代理
userMapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void queryUserById() {
User user = this.userMapper.queryUserById(8);
System.out.println(user);
}
@Test
public void queryAllUser(){
List users = this.userMapper.queryAllUser();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void deleteUserById(){
this.userMapper.deleteUserById(8);
}
@Test
public void addUser(){
User user=new User();
user.setUsername("jack");
user.setPassword("1234");
user.setAge(23);
user.setSex("男");
user.setEmail("[email protected]");
this.userMapper.addUser(user);
}
@Test
public void updateUser(){
User user=new User();
user.setId(10);
user.setUsername("rose");
user.setPassword("147852");
user.setAge(22);
user.setSex("女");
user.setEmail("[email protected]");
this.userMapper.updateUser(user);
}
}
修改后的接口:
public interface UserMapper {
/**
* @description:根据id查询用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 14:45
* @param: []
* @return: cn.itssl.pojo.User
**/
User queryUserById(int id);
/**
* @description:查询所有用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 14:45
* @param: []
* @return: java.util.List
**/
List queryAllUser();
/**
* @description:删除用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 14:46
* @param: []
* @return: void
**/
void deleteUserById(int id);
/**
* @description:添加用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 14:46
* @param:
* @return:
**/
void addUser(User user);
/**
* @description:根据id修改用户
* @author: Shenshuanglong
* @date: 2022/10/14 0014 14:47
* @param: [id]
* @return: void
**/
void updateUser(User user);
}
修改后的UserMapper.xml文件
delete
from t_user
where id = #{id}
insert into t_user
values (null, #{username}, #{password}, #{age}, #{sex}, #{email})
update t_user
set username=#{username},
password=#{password},
age=#{age},
sex=#{sex},
email=#{email} where id=#{id};
注意:因为没有了实现类,在关联xml映射文件之后需要让接口的方法名和sql映射文件中每条sql的id唯一标识的名称保持一致,否则会出现报错!!
可以发现,我们每次写查询语句的时候,必须要写一个resultType来指定一下返回值类型,然而每次都得写路径,太过于麻烦,那么Mybatis提供一个typeAliaseso类别名来简化开发。
注意:以上方式选其一即可,两个同时存在会出现冲突。
配置完之后,我们再修改UserMapper.xml文件,把原有的路径去掉,只留下实体类的类名
其实我们后开发中,不止一个环境,例如:开发环境,测试环境,发布环境等,可以在项目中设置多环境
配置完之后,我们在测试类的setUp方法中加入环境。
@Before
public void setUp() throws Exception {
//指定核心配置文件的位置
String resource = "mybatis-config.xml";
//加载核心配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
//构建sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,"dev");//使用指定环境
//获取SqlSession对象,SqlSession可以操作crud
sqlSession = sqlSessionFactory.openSession();
//动态代理
userMapper = sqlSession.getMapper(UserMapper.class);
}
其实在全局配置里面的mappers不只有resource一种获取映射文件的方式,还有其余两种,一个是class属性来获取,需要用.来分隔。另外一种就是通过导包的方式,但是必须满足两个条件:
1、接口名要与mapper的名一致
2、接口类要与mapper.xml文件放在同一目录中
select标签:发送select 语句
id属性:当前名称空间下的statement的唯一标识。必须。要求id和mapper接口中的方法的名字一致。
select 标签中
resultType:将结果集映射为java的对象类型必须要有(和 resultMap 二选一)
resultType使用注意:select查询出来 列名要和resultType指定的类型属性名对应,否则完成不了映射赋值。如果列名与类的属性名不一致则需要用自定义resultMap,和列名不一样的属性名要用resultMap的子标签id和result设置property、colum。parameterType:传入参数类型。可以省略,会自判断传入的是什么类型
id属性:当前名称空间下的statement的唯一标识(必须属性);
parameterType:传入的参数类型,可以省略。
INSERT INTO tb_user (id, user_name, sex)
VALUES (#{id}, #{userName}, #{sex})
id属性:当前名称空间下的statement的唯一标识(必须属性);
parameterType:传入的参数类型,可以省略
UPDATE tb_user SET user_name = #{userName}, sex = #{sex} WHERE id = #{id};
#{ 只是表示占位可以随便写 } 相当于 ? 前提一定是一个变量
delete from tb_user where id = #{id}