目录
2.5创建操作表的 sql 映射文件 BookMapper.xml
4.2 在sql映射文件的对应sql语句中使用自己定义的参数名
1.架构介绍(纯理论,可跳过)
1.1三层架构
mvc:web开发中,使用mvc架构模式。m:数据,v:视图,c:控制器。
m数据:来自数据库mysql,来自文件,来自网络。
v视图:现在使用jsp,html,css,js等,显示请求的处理结果,把m中数据显示出来。
c控制器:接收请求,调用servlce对象,显示请求的处理结果。现在使用servlet作为控制器。
三层架构:
1.持久层(数据库访问层):访问数据库或读取文件,访问网络。获取数据。对应 的包是dao,dao包中很多的xxxDao。--->MyBatis框架
2.业务逻辑层:处理业务逻辑,使用算法处理数据的。把数据返回给界面层。对应 的是servlce包,包中的很多的XXXService类。--->Spring框架
3.界面层(视图层):接收用户的请求,调用service,显示请求的处理结果。包含 了jsp,html,servlet等对象。对应的包是cotroller。--->SpringMVC框架
1.2 MyBatis框架
1.2.1 什么是MyBatis框架
mybatis:是一个持久层框架,原名是ibatis,2.13改名为MyBatis。MyBatis可以操 作数据库,对数据执行增删改查。可以看做高级的jdbc,解决了jdbc的缺点。
1.2.2 MyBatis可以做什么
1)注册驱动。
2)创建jdbc中的Connection,Statement,ResultSet
3)执行sql语句,得到ResultSet
4)处理ResultSet,把记录集中的数据转换为java对象,同时还能把java对象放到 List集合。
5)关闭资源
6)实现sql语句和java代码的解耦合。
2.使用MyBatis
2.1 安装
要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。
如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:
<dependencies>
<!--mysqly依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
</dependencies>
2.2 创建数据库与数据表
#创建数据表
CREATE TABLE `book_info` (
`book_id` int(11) NOT NULL AUTO_INCREMENT,
`book_name` varchar(20) NOT NULL,
`book_author` varchar(20) NOT NULL,
`book_price` int(11) NOT NULL,
`book_pub` varchar(20) NOT NULL,
PRIMARY KEY (`book_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1017 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
#插入数据
INSERT INTO `book_info` VALUES ('1001', '西游记', '吴承恩', '35', '清华大学出版社');
INSERT INTO `book_info` VALUES ('1002', '骆驼祥子', '老舍', '23', '新华出版社');
INSERT INTO `book_info` VALUES ('1003', '哈利比特', 'J.K.罗琳', '41', '人民出版社');
INSERT INTO `book_info` VALUES ('1004', '安徒生童话', '安徒生', '28', '人民文学出版社');
INSERT INTO `book_info` VALUES ('1005', '青铜葵花', '曹文轩', '32', '清华大学出版社');
INSERT INTO `book_info` VALUES ('1007', '红楼梦', '曹雪芹', '45', '人民教育出版社');
INSERT INTO `book_info` VALUES ('1008', '平凡的世界', '路遥', '56', '人民美术出版社');
INSERT INTO `book_info` VALUES ('1009', '1', '1', '1', '1');
INSERT INTO `book_info` VALUES ('1011', '哈哈', '哈', '48', '哈哈出版社');
INSERT INTO `book_info` VALUES ('1012', 'aaa', 'a', '18', 'aaa出版社');
INSERT INTO `book_info` VALUES ('1014', '0', '0', '188', '0');
INSERT INTO `book_info` VALUES ('1015', 'ccc', 'cc', '666', 'cc出版社');
2.3 配置mybatis的配置文件
mybatis会读取改文件的内容 完成连接数据库功能。
XML 配置文件中包含了对 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.cj.jdbc.Driver" />
<!-- 对应自己的数据库名-->
<property name="url" value="jdbc:mysql://localhost:3306/11-30?serverTimezone=Asia/Shanghai" />
<!-- 对应自己的数据库用户名-->
<property name="username" value="root" />
<!-- 对应自己的数据库密码-->
<property name="password" value="123@qwe" />
</dataSource>
</environment>
</environments>
</configuration>
2.4创建对应的实体类
package com.cys.entity;
/**
* @program: maven-mybatis01
* @description:
* @author: 曹乙山
* @create: 2021-11-30 14:59
**/
public class Book {
private int book_id;
private String book_name;
private String book_author;
private int book_price;
private String book_pub;
public Book() {
}
public Book(int book_id, String book_name, String book_author, int book_price, String book_pub) {
this.book_id = book_id;
this.book_name = book_name;
this.book_author = book_author;
this.book_price = book_price;
this.book_pub = book_pub;
}
public int getBook_id() {
return book_id;
}
public void setBook_id(int book_id) {
this.book_id = book_id;
}
public String getBook_name() {
return book_name;
}
public void setBook_name(String book_name) {
this.book_name = book_name;
}
public String getBook_author() {
return book_author;
}
public void setBook_author(String book_author) {
this.book_author = book_author;
}
public int getBook_price() {
return book_price;
}
public void setBook_price(int book_price) {
this.book_price = book_price;
}
public String getBook_pub() {
return book_pub;
}
public void setBook_pub(String book_pub) {
this.book_pub = book_pub;
}
@Override
public String toString() {
return "Book{" +
"book_id=" + book_id +
", book_name='" + book_name + '\'' +
", book_author='" + book_author + '\'' +
", book_price=" + book_price +
", book_pub='" + book_pub + '\'' +
'}';
}
}
2.5创建操作表的 sql 映射文件 BookMapper.xml
<?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">
<!--
mapper内编写对应的sql语句
namespace:填写路径,唯一索引 (当前可随意填写(合法))
<select> 查询标签 id:唯一索引(当前可随意填写(合法))
resultType:该表对应实体类的路径
-->
<!--
占位符为:#{参数名}
当方法参数为一个 基本类型或String 值时,参数名可以任意命名(合法)
当方法参数为实体类时,例如<insert> 对应的占位符内的参数名应与预计接收到的实体类属性名相同
当为多个参数时,见第二节 传递多个参数
-->
<mapper namespace="Mapper.BookMapper">
<select id="SelectOne" resultType="com.cys.entity.Book">
select * from book_info where book_id=#{id}
</select>
<select id="SelectAll" resultType="com.cys.entity.Book">
select * from book_info
</select>
<insert id="AddBook">
insert into book_info values(null,#{book_name},#{book_author},#{book_price},#{book_pub})
</insert>
<delete id="DeleteBook">
delete from book_info where book_id=#{book_id}
</delete>
<update id="UpdateBook">
update book_info set book_name=#{book_name},book_author=#{book_author},book_price=#{book_price},book_pub=#{book_pub} where book_id=#{book_id}
</update>
</mapper>
2.6 在配置文件里注册映射文件(写在最下方)
<!-- 写在</configuration>标签上面 -->
<mappers>
<!-- resource:对应映射文件的路径-->
<mapper resource="Mapper/BookMapper.xml"/>
</mappers>
</configuration>
2.7 创建测试类测试对应sql语句
2.7.1 引入测试类依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
2.7.2 在测试类中测试已映射的sql语句
@Test
public void test01() throws IOException {
/*
SqlSessionFactoryBuilder:这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。
SqlSessionFactory:SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
SqlSession:每个线程都应该有它自己的 SqlSession 实例。
SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
因此前三行代码目的便是创建出来Sqlsession,以便于执行已映射的SQL语句
*/
Reader reader = Resources.getResourceAsReader("Mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlSessionFactory.openSession();
/*
selectOne(s,o) s: 映射文件的namespace.方法标签的id o:实参
*/
Book book = session.selectOne("Mapper.BookMapper.SelectOne",1001);
System.out.println(book);
}
@Test
public void test03() throws IOException {
Reader reader = Resources.getResourceAsReader("Mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlSessionFactory.openSession();
Book book = new Book(0,"ccc","cc",18,"cc出版社");
int row = session.insert("Mapper.BookMapper.AddBook",book);
System.out.println("影响行数:"+row);
session.commit();
/*
session.commit(): 提交事务 查询是从数据库中查出数据,因此可以不提交事务,但增、删、改操作会对数据库造成改动,因此需要手动提交数据。
若在SqlSession session = sqlSessionFactory.openSession(true); 里面加了true则可以完成自动提交事务,不需要手动提交。
*/
}
执行结果:
至此,MyBatis初级使用结束,下面是实际开发中如何使用MyBatis。
3.使用Dao层开发
3.1 为什么使用Dao层开发
在上面的映射文件中的 namespace 与sql语句的 id 都是随便写,然后在测试的时候使用namespace+id的方式定位到具体sql语句,并结合固定的方法:selectOne()、selectList()等等,由于我们习惯于自己命名自己的sql方法名,因此在实际开发中 我们会引入Dao层来增强代码的可读性使逻辑更加清晰。
注意:在使用Dao层之后 映射文件的nameSpace对应的是dao接口的具体路径,sql语句id对应的是dao接口的方法名。
3.2 创建Dao接口
package com.aaa.qy145.ten.cys.dao;
import com.aaa.qy145.ten.cys.entity.Book;
import java.util.List;
/**
* @program: maven-mybatis08
* @description:
* @author: 曹乙山
* @create: 2021-12-02 08:36
**/
public interface BookDao {
int addBook(Book book);
int deleteBook(int id);
int updateBook(Book book);
List<Book> selectAll(Book book);
}
3.3对应的 sql映射文件
<?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">
<!--
!!!!!!!!!!!!!!!!注意!!!!!!!!!!!!!!!!!!!!
mapper内编写对应的sql语句
namespace: 需要对应Dao接口的路径
<select> 查询标签 id:需要对应Dao接口的方法名
resultType:该表对应实体类的路径
-->
<mapper namespace="com.aaa.qy145.ten.cys.dao.BookDao">
<insert id="addBook">
insert into book_info values (null,#{book_name},#{book_author},#{book_price},#{book_pub})
</insert>
<update id="updateBook">
update book_info set book_name=#{book_name},book_author=#{book_author},book_price=#{book_price},book_price=#{book_pub}
</update>
<delete id="deleteBook">
delete from book_info where book_id=#{book_id}
</delete>
<select id="selectAll" resultType="com.aaa.qy145.ten.cys.entity.Book">
select * from book_info
</select>
</mapper>
3.4在mybatis.xml中注册sql映射文件
<mappers>
<!-- resource:对应映射文件的路径-->
<mapper resource="Mapper/BookMapper.xml"/>
</mappers>
3.5在测试类中测试
@Test
public void test01(){
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlSessionFactory.openSession();
Book book = new Book(0,"人间失格","太宰治",15,"xx出版社");
/*
session.getMapper(Dao接口名.class) 可以返回一个Dao接口的实体类
通过 实体类.方法名(参数) 即可调用自己命名的方法
*/
BookDao bookDao = session.getMapper(BookDao.class);
int row = bookDao.addBook(book);
System.out.println("影响行数:"+row);
session.commit();
}
执行结果:
4.传递多个参数
4.1在Dao接口中定义多个参数的方法
4.2 在sql映射文件的对应sql语句中使用自己定义的参数名
4.3在测试类中测试
测试结果:
5.特殊字符
某些特殊字符 在映射文件里面会报错,因此需要使用一些别的方法
5.1 使用转移字符
5.2使用<![CDATA[ 特殊字符或SQL语句 ]]>
接下来正常测试运行即可。。。
6.MyBatis的优化
6.1 引入db属性文件----更方便的修改数据库
(1)定义一个数据库属性文件. properties
(2)在mybatis配置文件中引入属性文件并使用相应的key
6.2 引入日志文件。----更好的显示sql语句
(1)添加对应映射
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
(2)引入日志的配置文件 log4j.properties
注意:文件名必须为 log4j.properties
### 设置###
log4j.rootLogger = debug,stdout,D,E
### 输出信息到控制抬 ###
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 = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### 输出DEBUG 级别以上的日志到=D://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = D://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 输出ERROR 级别以上的日志到=D://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =D://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
运行测试类时 与上面的运行结果 一样出现sql语句
6.3 解决列名和属性名不一致
之前实体类的属性名都与数据库的列名相同 当出现不同的时候会发现对应字段为null,本次提供两种解决方法。
修改实体类中的属性名 与数据库列名不一致
(1)为查询的列起别名;让别名和属性名一致。
/*
为数据库的列名起别名 别名对应着属性名 便可解决对应属性为null问题
*/
<select id="selectAll" resultType="com.aaa.qy145.ten.cys.entity.Book">
select book_id id,book_name name,book_author author,book_price price,book_pub pub from book_info
</select>
(2) 使用resultMap标签 来完成属性和列的映射关系。
/*
<resultMap>: id:随意命名(合法)与<select>的 resultMap属性相对应
type:该表对应实体类的路径
autoMapping:<resultMap>标签内可以只写属性名与列名不同的对应关系
相同的可以通过该属性自动对应
<id/>: 数据表主键与实体类对应的属性名 使用该标签 (必写)
<result/>: 非主键的列名与属性名的对应 使用该标签
(与列名相同的属性名可以不写)
property:该标签属性对应的是 实体类中的属性名
colum: 该标签属性对应的是 数据表的列名
<select>中的 resultMap属性 与<resultMap>标签的 id属性相对应
*/
<resultMap id="mymap" type="com.aaa.qy145.ten.cys.entity.Book" autoMapping="true">
<id property="id" column="book_id"/>
<result property="name" column="book_name"/>
<result property="author" column="book_author"/>
<result property="price" column="book_price"/>
<result property="pub" column="book_pub"/>
</resultMap>
<select id="selectAll" resultMap="mymap">
select * from book_info
</select>
7. 模糊查询
7.1 sql映射文件中的 sql语句
<select id="selectbysomename" resultMap="mymap">
select * from book_info where book_author like concat('%',#{author},'%')
</select>
/*
concat(): 字符串拼接
*/
7.2 测试类测试sql语句
@Test
public void test03(){
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlSessionFactory.openSession();
BookDao bookDao = session.getMapper(BookDao.class);
List<Book> list = bookDao.selectbysomename("曹");
for (Book b:list){
System.out.println(b);
}
}
运行结果:
8. 分页查询
8.1 引入对应的依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
8.2 在mybatis.xml配置文件中加入拦截
<!-- 放到<properties/> 标签之后 <environments>标签之前-->
<plugins>
<!-- com.github.pagehelper 为PageHelper类所在的包名-->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="param1" value="value1"/>
</plugin>
</plugins>
8.3 使用分页功能
@Test
public void test03(){
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlSessionFactory.openSession();
//使用分页功能 pageNum:显示第几页 pageSize:每一页有几个数据
PageHelper.startPage(2,2);
BookDao bookDao = session.getMapper(BookDao.class);
List<Book> list = bookDao.selectbysomename("曹");
//可以把查询的结果封装到PageInfo类中 包含你想要的任何信息
PageInfo pageInfo = new PageInfo(list);
System.out.println("总条数:"+pageInfo.getTotal());
System.out.println("当前页:"+pageInfo.getList());
}
结果显示:
9.多表查询与动态sql
见下篇~~~