//相对路径
//只要路径中以/开头的都叫做全路径. 从项目根目录(WebContent)出发找到其他资源的过程
//只要不以/开头都是相对路径,相对路径是从当前资源出发找到其他资源的过程
//如果请求转发 / 表示WebContent目录
//如果是重定向,静态资源引用,必须 css引用时 其中 /都表示的是tomcat的webapps文件夹根目录
//服务器根目录
<configuration>
<environments default="default">
<environment id="default">
<transactionManager type="JDBC">transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="com/juruo/mapper/FlowerMapper.xml">mapper>
mappers>
configuration>
没有名称要求和地址要求,在全局配置文件中引入DTD或schema
文件作用:
编写需要执行的SQL命令,把xml理解成实现类。
FlowerMapper.xml
<mapper namespace="a.b">
<select id="selAll" resultType="com.juruo.pojo.Flower">
select * from flower
select>
<select id="selById" resultType="int">
select count(*) from flower
select>
<select id="c" resultType="com.juruo.pojo.Flower">
select *from flower
select>
mapper>
package com.juruo.servlet;
import com.juruo.pojo.Flower;
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 org.apache.tools.ant.types.Resource;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.ResourceBundle;
@WebServlet(name = "TestServlet",
urlPatterns = "/test")
public class TestServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream is = Resources.getResourceAsStream("mybatis.xml");
//使用工厂设计模式
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//生产SqlSession
SqlSession session = factory.openSession();
List<Flower> list = session.selectList("a.b.selAll");
for(Flower flower : list){
resp.getWriter().write(flower.toString() + "
");
}
session.close();
}
}
Flower.java
package com.juruo.pojo;
public class Flower {
private int id;
private String name;
private double price;
private String production;
public Flower(int id, String name, double price, String production) {
this.id = id;
this.name = name;
this.price = price;
this.production = production;
}
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 double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getProduction() {
return production;
}
public void setProduction(String production) {
this.production = production;
}
@Override
public String toString() {
return this.id + " " + this.name + " " + this.price + " " + this.production;
}
}
全局配置文件中内容
<transactionManager type="JDBC">transactionManager>
<dataSource type="POOLED">dataSource> type属性
在内存中开辟一块空间,存放多个数据库连接对象
JDBC Tomcat Pool 直接由tomcat产生数据库连接池
使用数据库连接池的目的
在高频率访问数据库时,使用数据库连接池可以降低服务器系统压力,提升程序运行效率,小型项目不适用数据库连接池。
实现JDBC Tomcat Pool的步骤
在web项目的META-INF中存放context.xml
适用于查询结果都需要遍历的需求
List<Flower> list = session.selectList("a.b.selAll");
for(Flower flower : list){
resp.getWriter().write(flower.toString() + "
");
}
适用于返回结果只是变量或一行数据时
int count = session.selectOne("a.b.selById");
System.out.println(count);
适用于需求需要在查询结果中通过某列的值取到这行数据的需求
Map<Object,Object> map = session.selectMap("a.b.c","name");
System.out.println(map);
在项目中编写 System.out.println();输出到控制台,当项目发布到 tomcat 后,没有控制台(在命令行界面能看见.),不容易观察一些输出结果。
log4j 作用:
不仅能把内容输出到控制台,还能把内容输出到文件中.便于观察结果。
/*1.导入 log4j-xxx.jar
2.在 src 下新建 log4j.properties(路径和名称都不允许改变)
2.1 ConversionPattern :写表达式
2.2 log4j.appender.LOGFILE.File 文件位置及名称(日
志文件扩展名.log)
*/
log4j.rootCategory=INFO, CONSOLE ,LOGFILE
log4j.logger.a.b=DEBUG
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%C %p %m %n
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=E:/my.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%C %m %L %n
fatal(致命错误) > error (错误) > warn (警告) > info(普通信息) > debug(调试信息)
在 log4j.properties 的第一行中控制输出级别
%C 包名+类名
%d{YYYY-MM-dd HH:mm:ss} 时间
%L 行号
%m 信息
%n 换行
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
1.命名级别(包级别): <mapper>namespace 属性中除了最后一个类名
//例如 namespace=”com.juruo.mapper.PeopleMapper” 其中包级别为com.juruo.mapper ,需要在 log4j.propeties中
先在总体级别调成 Error 不输出无用信息
在设置某个指定位置级别为 DEBUG
2.类级别
namespace 属性值 ,namespace 类名
3.方法级别
使用 namespace 属性值+标签 id 属性值
示例
People p = session.selectOne("a.b.selById",1);
System.out.println(p);
<select id="selById" resultType="com.juruo.pojo.People" parameterType="int">
select * from people where id = #{0}
select>
/*
#{} 获取参数的内容支持 索引获取,param1 获取指定位置参数, 并且 SQL 使用?占位符
${} 字符串拼接不使用?,默认找${内容}内容的 get/set 方法,如
果写数字,就是一个数字
*/
/*
1.?不允许在关键字前后进行数学运算,需要在代码中计算完成后传递到 mapper.xml 中
2.在 java 代码中计算
*/
//显示几个
int pageSize = 2;
//第几页
int pageNumber = 2;
//如果希望传递多个参数,可以使用对象或 map
Map<String,Object> map = new HashMap<>();
map.put("pageSize", pageSize);
map.put("pageStart", pageSize*(pageNumber-1));
List<People> p = session.selectList("a.b.page",map);
在 mapper.xml 中代码
<select id="page" resultType="com.juruo.pojo.People" parameterType="map">
select * from people limit #{pageStart},#{pageSize}
select>
<typeAliases>
<typeAlias type="com.juruo.pojo.People" alias="peo"/>
typeAliases>
mapper.xml 中 peo 引用 People 类
<select id="page" resultType="peo" parameterType="map">
select * from people limit #{pageStart},#{pageSize}
select>
mybatis.xml 中配置
<typeAliases>
<package name="com.juruo.pojo"/>
typeAliases>
mapper.xml 中通过类名引用
<select id="page" resultType="people" parameterType="map">
select * from people limit #{pageStart},#{pageSize}
select>
功能:从应用程序角度出发,软件具有哪些功能.
业务:完成功能时的逻辑.对应 Service 中一个方法
事务:从数据库角度出发,完成业务时需要执行的 SQL 集合,统称一个事务.
//事务回滚.如果在一个事务中某个 SQL 执行事务,希望回归到事务的原点,保证数据库数据的完整性.
每一个 SqlSession 默认都是不自动提交事务
session.commit()提交事务
openSession(true);自动提交,setAutoCommit(true);
JDBC 中 executeUpdate()执行新增,删除,修改的 SQL.返回值 int, 表示受影响的行数
mybatis 中<insert> <delete> <update>标签没有 resultType 属性, 认为返回值都是 int
如果出现异常,应该 session.rollback()回滚事务.
<insert id="ins" parameterType="People">
insert into people values(default,#{name},#{age})
insert>
通过 session.insert()调用新增方法
int index1 = session.insert("a.b.ins", p);
if(index1>0){
System.out.println("成功");
}else{
System.out.println("失败");
}
<update id="upd" parameterType="People">
update people set name = #{name} where id = #{id}
update>
编写代码
People peo = new People();
peo.setId(3);
peo.setName("王五");
int index = session.update("a.b.upd", peo);
if(index>0){
System.out.println("成功");
}else{
System.out.println("失败");
}
session.commit();
<delete id="del" parameterType="int">
delete from people where id = #{0}
delete>
编写代码
int del = session.delete("a.b.del",3);
if(del>0){
System.out.println("成功");
}else{
System.out.println("失败");
}
session.commit();
作用
实现创建一个接口后把mapper.xml由mybatis 生成接口的实现类,通过调用接口对象就可以获取 mapper.xml 中编写的 sql
后面 mybatis 和 spring 整合时使用的是这个方案.
实现步骤:
1.创建一个接口
1.1接口包名和接口名与 mapper.xml 中<mapper>namespace相同
1.2接口中方法名和 mapper.xml 标签的 id 属性相同
2.在 mybatis.xml 中使用<package>进行扫描接口和 mapper.xml
<mappers>
<package name="com.juruo.mapper"/>
mappers>
在 com.bjsxt.mapper 下新建接口
public interface LogMapper {
List<Log> selAll();
}
在 com.bjsxt.mapper 新建一个 LogMapper.xml
<mapper namespace="com.bjsxt.mapper.LogMapper">
<select id="selAll" resultType="People">
select * from people
select>
mapper>
在接口中声明方法
List<Log> selByAccInAccout(String accin,String accout);
在 mapper.xml 中添加
<select id="selByAccInAccout" resultType="log" >
select * from log where accin=#{0} and accout=#{1}
select>
在接口中声明方法
/**
* mybatis 把参数转换为 map 了,其中@Param("key") 参数内
容就是 map 的 value
* @param accin123
* @param accout3454235
* @return
*/
List<Log> selByAccInAccout(@Param("accin") String
accin123,@Param("accout") String accout3454235);
在 mapper.xml 中添加
<select id="selByAccInAccout" resultType="log" >
select * from log where accin=#{accin} and accout=#{accout}
select>
package com.juruo.servlet;
import com.juruo.mapper.PeopleMapper;
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 com.juruo.pojo.People;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
@WebServlet(name = "TestServlet",
urlPatterns = "/test")
public class TestServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream is = Resources.getResourceAsStream("mybatis.xml");
//使用工厂设计模式
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//生产SqlSession
SqlSession session = factory.openSession();
PeopleMapper logMapper = session.getMapper(PeopleMapper.class);
List<People> list = logMapper.selAll();
for (People p : list) {
System.out.println(p.toString());
}
session.close();
}
}
<select id="selByAccinAccout" resultType="log">
select * from log where 1=1
<if test="accin!=null and accin!=''">
and accin=#{accin}
if>
<if test="accout!=null and accout!=''">
and accout=#{accout}
if>
select>
<select id="selByAccinAccout" resultType="log">
select * from log
<where>
<if test="accin!=null and accin!=''">
and accin=#{accin}
if>
<if test="accout!=null and accout!=''">
and accout=#{accout}
if>
where>
select>
<select id="selByAccinAccout" resultType="log">
select * from log
<where>
<choose>
<when test="accin!=null and accin!=''">
and accin=#{accin}
when>
<when test="accout!=null and accout!=''">
and accout=#{accout}
when>
choose>
where>
select>
用在修改 SQL 中 set 从句
<update id="upd" parameterType="log" >
update log
<set>
id=#{id},
<if test="accIn!=null and accIn!=''">
accin=#{accIn},
if>
<if test="accOut!=null and accOut!=''">
accout=#{accOut},
if>
set>
where id=#{id}
update>
<update id="upd" parameterType="log">
update log
<trim prefix="set" suffixOverrides=",">
a=a,
trim>
where id=100
update>
<select id="selByLog" parameterType="log" resultType="log">
<bind name="accin" value="'%'+accin+'%'"/>
#{money}
select>
标签/*
1 循环参数内容,还具备在内容的前后添加内容,还具备添加分隔符功能
2 适用场景:in 查询中.批量新增中(mybatis 中 foreach 效率比较低)
如果希望批量新增,SQL 命令
*/
insert into log VALUES(default,1,2,3),(default,2,3,4),(default,3,4,5)
/*openSession()必须指定
底层 JDBC 的 PreparedStatement.addBatch();
*/
factory.openSession(ExecutorType.BATCH);
/*
3.示例
collectino=”” 要遍历的集合
item 迭代变量, #{迭代变量名}获取内容
open 循环后左侧添加的内容
close 循环后右侧添加的内容
separator 每次循环时,元素之间的分隔符
*/
<select id="selIn" parameterType="list" resultType="log">
select * from log where id in
<foreach collection="list" item="abc" open="("close=")" separator=",">
#{abc}
</foreach>
</select>
和
<sql id="mysql">
id,accin,accout,money
sql>
<select id="">
select <include refid="mysql">include>
from log
select>
final ThreadLocal<String> threadLocal = newThreadLocal<>();
threadLocal.set("测试");
new Thread(){
public void run() {
String result = threadLocal.get();
System.out.println("结果:"+result);
};
}.start();
1 同一个 SqlSession 对象调用同一个<select>时,只有第一次访问
数据库,第一次之后把查询结果缓存到 SqlSession 缓存区(内存)中
2 缓存的是 statement 对象.(简单记忆必须是用一个<select>)
在 myabtis 时一个<select>对应一个 statement 对象
3 有效范围必须是同一个 SqlSession 对象
1 步骤一: 先去缓存区中找是否存在 statement
2 步骤二:返回结果
3 步骤三:如果没有缓存 statement 对象,去数据库获取数据
4 步骤四:数据库返回查询结果
5 步骤五:把查询结果放到对应的缓存区中
<cache readOnly="true">cache>
5 当 SqlSession 对象 close()时或 commit()时会把 SqlSession 缓存的数据刷(flush)到SqlSessionFactory 缓存区中
1 业务装配.对两个表编写单表查询语句,在业务(Service)把查询的两个结果进行关联.
2 使用 Auto Mapping 特性,在实现两表联合查询时通过别名完成映射.
3 使用 MyBatis 的<resultMap>标签进行实现.
1.单个对象
2.多个对象
1. <resultMap>标签写在mapper.xml中,由程序员控制SQL查询结果与实体类的映射关系.
默认 MyBatis 使用 Auto Mapping 特性.
2. 使用<resultMap>标签时,<select>标签不写 resultType 属性,而是使用 resultMap 属性引用<resultMap>标签.
3. 使用 resultMap 实现单表映射关系
public class Teacher{
private int id1;
private String name1;
}
3.3 mapper.xml代码
<resultMap type="teacher" id="mymap">
<id column="id" property="id1" />
<result column="name" property="name1"/>
resultMap>
<select id="selAll" resultMap="mymap">
select * from teacher
select>
/*
1 N+1 查询方式,先查询出某个表的全部信息,根据这个表的信息查询另一个表的信息.
2 与业务装配的区别:
在 service 里面写的代码,由 mybatis 完成装配
3 实现步骤:
在 Student 实现类中包含了一个 Teacher 对象
在 StudentMapper 中
装配一个对象时使用
property: 对象在类中的属性名
select:通过哪个查询查询出这个对象的信息
column: 把当前表的哪个列的值做为参数传递给另一个查询
大前提使用 N+1 方式.时如果列名和属性名相同可以不配置,使用 Auto mapping 特性.但是 mybatis 默认只会给列专配一次*/
public class Student {
private int id;
private String name;
private int age;
private int tid;
private Teacher teacher;
}
/*在 TeacherMapper 中提供一个查询*/
<select id="selById" resultType="teacher" parameterType="int">
select * from teacher where id=#{0}
select>
<resultMap type="student" id="stuMap">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="tid" column="tid"/>
<association property="teacher"
select="com.bjsxt.mapper.TeacherMapper.selById" column="tid">
association>
resultMap>
<select id="selAll" resultMap="stuMap">
select * from student
select>
把上面代码简化
<resultMap type="student" id="stuMap">
<result column="tid" property="tid"/>
<association property="teacher" select="com.bjsxt.mapper.TeacherMapper.selById" column="tid">
association>
resultMap>
<select id="selAll" resultMap="stuMap">
select * from student
select>
<resultMap type="Student" id="stuMap1">
<id column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="age" property="age"/>
<result column="tid" property="tid"/>
<association property="teacher" javaType="Teacher" >
<id column="tid" property="id"/>
<result column="tname" property="name"/>
association>
resultMap>
<select id="selAll1" resultMap="stuMap1">
select s.id sid,s.name sname,age age,t.id
tid,t.name tname FROM student s left outer join teacher
t on s.tid=t.id
select>
1 N+1:需求不确定时.
2 联合查询:需求中确定查询时两个表一定都查询.
1 举例:学生中有 3 条数据
2 需求:查询所有学生信息级授课老师信息
3 需要执行的 SQL 命令
1 查询全部学生信息:select * from 学生
2 执行 3 遍 select * from 老师 where id=学生的外键
4 使用多条 SQl 命令查询两表数据时,如果希望把需要的数据都查询出来,需要执行 N+1 条 SQl 才能把所有数据库查询出来.
5 缺点:
效率低
6 优点:
如果有的时候不需要查询学生是同时查询老师.只需要执行一个 select * from student;
7 适用场景: 有的时候需要查询学生同时查询老师,有的时候只需要查询学生.
8 如果解决 N+1 查询带来的效率低的问题
默认带的前提: 每次都是两个都查询.
使用两表联合查询.
/*
1. 在 Teacher 中添加 List
*/
public class Teacher {
private int id;
private String name;
private List<Student> list;
}
<select id="selByTid" parameterType="int" resultType="student">
select * from student where tid=#{0}
select>
<resultMap type="teacher" id="mymap">
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="list"
select="com.bjsxt.mapper.StudentMapper.selByTid"
column="id">collection>
resultMap>
<select id="selAll" resultMap="mymap">
select * from teacher
select>
1.在 teacherMapper.xml 中添加
<resultMap type="teacher" id="mymap1">
<id column="tid" property="id"/>
<result column="tname" property="name"/>
<collection property="list" ofType="student" >
<id column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="age" property="age"/>
<result column="tid" property="tid"/>
collection>
resultMap>
<select id="selAll1" resultMap="mymap1">
select t.id tid,t.name tname,s.id sid,s.name
sname,age,tid from teacher t LEFT JOIN student s on
t.id=s.tid;
select>
<select id="selAll" resultType="student">
select t.id `teacher.id`,t.name
`teacher.name`,s.id id,s.name name,age,tid
from student s LEFT JOIN teacher t on t.id=s.tid
select>
<package/>
<mapper class=””/>
@Select("select * from teacher")
List<Teacher> selAll();
@Insert("insert into teacher values(default,#{name})")
int insTeacher(Teacher teacher);
@Update("update teacher set name=#{name} where id=#{id}")
int updTeacher(Teacher teacher);
@Delete("delete from teacher where id=#{0}")
int delById(int id);
/*
1 以 N+1 举例
2 在 StudentMapper 接口添加查询
*/
@Select("select * from student where tid=#{0}")
List<Student> selByTid(int tid);
/*
3 在 TeacherMapper 接口添加
1 @Results() 相当于
2 @Result() 相当于 或
@Result(id=true) 相当与
3 @Many() 相当于
4 @One() 相当于
*/
@Results(value={
@Result(id=true,property="id",column="id"),
@Result(property="name",column="name"),
@Result(property="list",column="id",many=@Many(select="com.bjsxt.mapper.StudentMapper.selByTid"))
})
@Select("select * from teacher")
List<Teacher> selTeacher();
1 Resources MyBatis 中 IO 流的工具类
加载配置文件
2 SqlSessionFactoryBuilder() 构建器
作用:创建 SqlSessionFactory 接口的实现类
3 XMLConfigBuilder MyBatis 全局配置文件内容构建器类
作用负责读取流内容并转换为 JAVA 代码.
4 Configuration 封装了全局配置文件所有配置信息.
全局配置文件内容存放在 Configuration 中
5 DefaultSqlSessionFactory 是SqlSessionFactory接口的实现类
6 Transaction 事务类
每一个 SqlSession 会带有一个 Transaction 对象.
7 TransactionFactory 事务工厂
负责生产 Transaction
8 Executor MyBatis 执行器
作用:负责执行 SQL 命令
相当于 JDBC 中 statement 对象(或 PreparedStatement或 CallableStatement)
默认的执行器 SimpleExcutor
批量操作 BatchExcutor
通过 openSession(参数控制)
9 DefaultSqlSession 是 SqlSession 接口的实现类
10 ExceptionFactory MyBatis 中异常工厂
2.文字解释
1. 在 MyBatis 运行开始时需要先通过 Resources 加载全局配置文件.下面需要实例化 SqlSessionFactoryBuilder 构建器.帮助 SqlSessionFactory 接口实现类 DefaultSqlSessionFactory.
2. 在实例化 DefaultSqlSessionFactory 之前需要先创建 XmlConfigBuilder解析全局配置文件流,并把解析结果存放在 Configuration 中.之后把Configuratin 传递给 DefaultSqlSessionFactory.到此 SqlSessionFactory 工厂创建成功.
3. 由 SqlSessionFactory 工厂创建 SqlSession.
4. 每次创建 SqlSession 时,都需要TransactionFactory 创建 Transaction对象,同时还需要创建 SqlSession 的执行器 Excutor,最后实例化DefaultSqlSession,传递给 SqlSession 接口.
5. 根据项目需求使用 SqlSession 接口中的 API 完成具体的事务操作. 如果事务执行失败,需要进行 rollback 回滚事务. 如果事务执行成功提交给数据库.关闭 SqlSession