1 引言
2 MyBatis介绍
3 环境搭建
4 入门演示(MyBatis开发步骤)
5 CRUD
6 配置细节
第一阶段(JavaSE)
- java基本语法
- 流程控制
- 面向对象
- 常用类
集合
- 异常
- IO
- 多线程
- JDK新特性
第二阶段(JavaWeb)
前端
- html
- css
- js --> JQuery
- boostrap
- layui
- 页面元素
- 内置模块
- 服务器端
- Maven
- 项目结构(java/javaweb)
- pom依赖文件
- Tomcat
- Servlet
- 接收请求
- 做出响应
- 数据库
- 基本语句(
DDL,DML, DQL
) 牛客网-复习- 函数
- 索引,事务,存储引擎
- JDBC
- DBUtil
- 项目
- 项目整个开发流程
- 项目结构
- 项目开发的三层架构
- 项目中前端后端如何交互
- 如何调试
- 输出语句
- idea - debug
- 用过之后及时取消
- 浏览器 - debug
- 多看日志(控制台,上下翻开)
- 按照功能逻辑,从头到尾去讲一遍
SSM --> Spring,SpringMVC,Mybatis , 目前市面很流行的开发框架
什么是框架?
就是为了简化开发步骤,提高开发效率
MyBatis --> 数据访问层框架,把之前的JDBC再封装一下,变得简单易用
SpringMVC --> 控制层框架,基于spring,把之前Servlet再封装一下,变得简单易用
Spring —> 是一个大型容器,是一个"管家",管理项目中所有"对象"(javabean)(不是业务层框架)
特别注意: 框架只是提高开发效率,但是具体业务逻辑,还要自己写
官网: mybatis – MyBatis 3 | Introduction
MyBatis本是apache的一个开源项目
iBatis
,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis
。2013年11月迁移到Github。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。用于操作数据库。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。
MyBatis 避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。且有缓存机制,可以提高查询效率。
Mybatis是一个
半ORM框架
,可以消除JDBC的代码和步骤,让开发者只关注SQL
本身。
ORM是对象关系映射,是指数据库表和java实体类一一对应.
半ORM框架,还是需要写SQL,由框架帮你完成映射
完全ORM框架,连SQL都不需要写,只需要遵循ORM的要求,就会自动生成SQL完成映射(Hibernate,JPA等)
官网入门手册中说明如下:
- 创建项目
- 加入依赖
- 执行SQL
- XML
XML 配置文件
中包含了对MyBatis 系统的核心设置
,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)- 获得SqlSessionFactoryBuilder
- 获得SqlSessionFactory
- 获得SqlSession
- 执行SQL
自己确定开发步骤
- 确定表结构和数据
- 创建项目,加入依赖
- 确定实体类
写mybatis核心配置文件
写Mapper接口
写Mapper接口对应的映射文件XML
核心配置文件,加载映射文件
- 通过核心配置文件,获得SqlSessionFactoryBuilder
- 通过SqlSessionFactoryBuilder 获得 SqlSessionFactory
- 通过SqlSessionFactory获得SqlSession
- SqlSession获得接口文件的代理对象
通过代理对象调用方法执行
create database 2307_mybatis;
create table tb_user(
id int primary key auto_increment,
username varchar(50),
password varchar(50),
phone varchar(50),
createTime date,
money double
)default charset = utf8;
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.6version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.12version>
dependency>
dependencies>
idea安装插件
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
private int id;
private String username;
private String password;
private String phone;
private Date createTime;
private double money;
private int sex;
}
名字: 名称任意,一般有这么几种: sqlconfing.xml或者mybatis-config.xml
等
位置:resources下
内容:(第一次从官网拷贝)
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://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/database_name"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
<mappers>
mappers>
configuration>
以前需要创建dao层包结构,创建XxxDao接口,以及XxxDaoImpl实现类
现在使用mybatis后,dao层包结构命名为mapper,在包下创建XxxMapper.java接口文件
为什么要?
- 以前是写接口的实现类,自己在类中自己写JDBC步骤,完成CRUD操作;现在,需要使用Mybatis的映射文件,我们需要在配置文件中写sql,剩下的动作mybatis完成.即不需要再写实现类.而是需要创建一个接口的映射文件
写到哪?
一种写到resources下 (今天用这种)
另一种,可以写在com.qf.mapper包下(后续再用)
名字?
- 与接口文件一样UserMapper.xml
怎么写?如下
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.mapper.UserMapper">
<select id="findUserById" resultType="com.qf.model.User">
select * from tb_user where id = #{id}
select>
mapper>
package com.qf.test;
import com.qf.mapper.UserMapper;
import com.qf.model.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc
*/
public class TestMybatis {
// TODO 优化
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 不用背,知道有这一步,下次直接复制
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession( );
// BlogMapper mapper = session.getMapper(BlogMapper.class);
// Blog blog = mapper.selectBlog(101);
// 利用的动态代理技术,获得UserMapper的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 调用方法干活
User user = userMapper.findUserById(1);
System.out.println(user);
}
}
以前测试代码,都需要创建主方法,但是一个主方法只能完成一个功能! 再有新功能,就需要抽取方法,再继续再main方法中再写 —>这些写很麻烦
测试,就可以使用单元测试来测试代码,
每个方法都可以独立执行
,还可以抽取一些公用代码,减少重复
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13version>
<scope>testscope>
dependency>
在测试包下,创建类,定义方法,加上注解即可使用
// 方法不能是static,不能有返回值,不能有参数
@Test
public void selectById() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 不用背,知道有这一步,下次直接复制
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession( );
// BlogMapper mapper = session.getMapper(BlogMapper.class);
// Blog blog = mapper.selectBlog(101);
// 利用的动态代理技术,获得UserMapper的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 调用方法干活
User user = userMapper.findUserById(1);
System.out.println(user);
}
SqlSession sqlSession = null;
@Before // 该注解一加,该方法就会在每个单元测试方法【执行前】执行
public void init() throws IOException {
System.out.println("前前前前前前" );
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 不用背,知道有这一步,下次直接复制
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得SqlSession
sqlSession = sqlSessionFactory.openSession( );
}
略,详情看入门演示
【特别注意:ORM时字段名要和实体类属性一致,否则封装失败】
<select id="findUserById" resultType="com.qf.model.User">
select id,username,password,phone,createTime,sex,money from tb_user where id = #{id}
select>
设计查询接口方法
public interface UserMapper {
List<User> findAll();
}
映射文件
<select id="findAll" resultType="com.qf.model.User">
select * from tb_user
select>
测试
@Test
public void selectAll() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 不用背,知道有这一步,下次直接复制
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession( );
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.findAll( );
for (User user : list) {
System.out.println(user );
}
}
需求: 通过用户名和密码查询
接口方法
public interface UserMapper {
User findUserByLogin(String username,String password);
}
映射文件
<select id="findUserByLogin" resultType="com.qf.model.User">
select * from tb_user where username = #{param1} and password = #{param2}
select>
接口方法(参数加注解)
public interface UserMapper {
User findUserByLogin(@Param("username") String username, @Param("password") String password);
}
映射文件
<select id="findUserByLogin" resultType="com.qf.model.User">
select * from tb_user where username = #{username} and password = #{password}
select>
需求: 查询时,就要传递分页数据,又要传递模糊查询关键词,此时就可以使用Map来封装参数.
接口方法
public interface UserMapper {
User findUserByLoginMap(HashMap<String,Object> map);
}
映射文件
<select id="findUserByLoginMap" resultType="com.qf.model.User">
select * from tb_user where username = #{usernameKey} and password = #{passwordKey}
select>
测试
@Test
public void selectByLoginMap() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 不用背,知道有这一步,下次直接复制
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession( );
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<>( );
map.put("usernameKey","李豪");
map.put("passwordKey","123456");
User user = mapper.findUserByLoginMap(map);
System.out.println("user = " + user);
}
接口方法
public interface UserMapper {
int addUser(User user);
}
映射文件
<insert id="addUser">
insert into tb_user values (#{id},#{username},#{password},#{phone},#{createTime},#{money},#{sex});
insert>
测试
@Test
public void addUser() throws IOException {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User( );
user.setId(4);
user.setUsername("谢博");
user.setPassword("123456");
user.setPhone("1111");
user.setcreateTime(new Date());
user.setSex(1);
user.setMoney(10000 );
int i = mapper.addUser(user);
System.out.println(i > 0 ? "插入成功":"失败");
// mysql默认的事务是一句话就是一个独立的事务,自动提交
// mybatis封装后,默认事务没有自动提交
// 所以执行增删改需要手动提交事务
sqlSession.commit();
}
场景: 下订单,返回订单号
需求: 插入数据后,要得到数据库自增的id
实现: mysql提供一个sql语句select last_insert_id();
可以获得上次自增的id
int insertUser(User user);
<insert id="insertUser" parameterType="com.qf.model.User">
insert into user (username,password,createTime,phone,address)
values(#{username},#{password},#{createTime},#{phone},#{address})
<selectKey keyProperty="id" order="AFTER" resultType="int">
select last_insert_id()
selectKey>
insert>
/**
* 测试更新
*/
@Test
public void testInsert() throws IOException {
// 加载配置文件,变成流
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 通过流获得工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder( ).build(inputStream);
// 通过工厂SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession( );
// 通过SqlSession获得接口的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 创建一个对象
User user = new User( );
user.setUsername("喻去子");
user.setPassword("123456");
user.setAddress("中国 贵州 黔南布依族苗族自治州");
user.setcreateTime(new Date());
// 执行插入,其中会有主键回填
System.out.println("插入前: " +user );
int i = mapper.insertUser(user);
System.out.println(i );
// 提交
sqlSession.commit();
// 再次打印对象,此时对象中的id有值
System.out.println("插入后: " +user );
}
接口方法
public interface UserMapper {
int updateUser(User user);
}
映射文件
<update id="updateUser">
update tb_user set username=#{username},password=#{password},
phone= #{phone},createTime=#{createTime},money=#{money},sex=#{sex}
where id = #{id}
update>
测试
@Test
public void updateUser() throws IOException {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User( );
user.setId(4);
user.setUsername("谢大博");
user.setPassword("123456");
user.setPhone("22222");
user.setcreateTime(new Date());
user.setSex(2);
user.setMoney(20000 );
int i = mapper.updateUser(user);
System.out.println(i > 0 ? "更新成功":"失败");
// mysql默认的事务是一句话就是一个独立的事务,自动提交
// mybatis封装后,默认事务没有自动提交
// 所以执行增删改需要手动提交事务
sqlSession.commit();
}
自己写
Mapper.xml文件除了放在resources下,也可以放在其他位置,常见的会和接口放在一起
如上这样,那么在mybatis-config.xml中就可以这么写
<mappers>
<mapper resource="com/qf/mapper/UserMapper.xml"/>
mappers>
但是,IDEA默认不会加载这个UserMapper.xml,需要在POM.xml加入如下配置
在pom.xml文件最后追加< build >标签,以便可以将xml文件复制到classes中,并在程序运行时正确读取。
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>*.xmlinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
resources>
build>
如上配置,随着项目模块功能的增多,接口也多了起来,随之对应的映射文件也多了起来,如下
<mappers>
<mapper resource="com/qf/model/DogMapper.xml"/>
<mapper resource="com/qf/model/PigMapper.xml"/>
<mapper resource="com/qf/model/CatMapper.xml"/>
...
...
mappers>
怎么解决呢?
mybatis的全局配置文件提供了一个简单写法
<mappers>
<package name="com.qf.mapper"/>
mappers>
对于mybatis-config.xml的核心配置中,如果存在需要频繁改动的数据内容,可以提取到properties中。
#在resources下创建db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/example?useUnicode=true&characterEncpding=utf8
jdbc.username=root
jdbc.password=123456
修改mybatis-config.xml。
DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties" />
<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>
configuration>
为实体类定义别名,提高书写效率。
在映射XML文件时,resultType中的类名是全路径,设置了别名就可以简单只写类名即可
DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties ... />
<typeAliases>
<package name="com.qf.model"/>
typeAliases>
...
configuration>
场景描述:
数据库有个字段create_time,mybatis查询时封装数据到User类中时,User类的属性createTime没有值,是null , 为什么??
原因:
就是因为数据库列create_time和属性名createTime不一致,导致封装失败
解决方案:
开启mybatis中,数据库列,下划线转驼峰
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
官方解释:是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn, 默认是false,需要指定成true开启
映射文件,只写类名即可
<select id="findUserById" resultType="User">
select * from tb_user where id = #{id}
select>
pom.xml添加log4j依赖
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
创建在resources并命名为log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
特别注意: 可以配置只打印sql
log4j.rootLogger=info # 设置mapper接口类或者配置文件中mapper的路径为debug log4j.logger.com.qf.mapper=DEBUG,stdout # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
级别 | 描述 |
---|---|
ALL LEVEL | 打开所有日志记录开关;是最低等级的,用于打开所有日志记录。 |
DEBUG | 输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。 |
INFO | 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。 |
WARN | 输出警告信息;表明会出现潜在错误的情形。 |
ERROR | 输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。 |
FATAL | 输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。 |
OFF LEVEL | 关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。 |
mybatis-config.xml设置使用日志
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
然后在执行mybatis代码的是就会有日志输出
练习
1 代码重复2遍
作业
2 换一张表,再来一遍
思考
思考MyBatis到底帮你省了哪些事情?
接口文件和映射文件如何关联?
接口文件中方法又是如何和映射文件中的语句关联?
语句执行时入参都可以有哪些?有什么注意事项?
语句执行后返回的有哪些类型?
he.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
级别 | 描述 |
---|---|
ALL LEVEL | 打开所有日志记录开关;是最低等级的,用于打开所有日志记录。 |
DEBUG | 输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。 |
INFO | 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。 |
WARN | 输出警告信息;表明会出现潜在错误的情形。 |
ERROR | 输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。 |
FATAL | 输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。 |
OFF LEVEL | 关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。 |
mybatis-config.xml设置使用日志
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
然后在执行mybatis代码的是就会有日志输出
练习
1 代码重复2遍
作业
2 换一张表,再来一遍
思考
思考MyBatis到底帮你省了哪些事情?
接口文件和映射文件如何关联?
接口文件中方法又是如何和映射文件中的语句关联?
语句执行时入参都可以有哪些?有什么注意事项?
语句执行后返回的有哪些类型?