Mybatis框架+tomcat+Servlet

引入: 查询员工的结果封装到emp对象中,一条记录对应一个Emp对象,将所有Emp对象封装为了一个List集合.
在这里插入图片描述
在这里插入图片描述

补充:

1️⃣XML: Extensible Markup Language 可扩展标记语言

  • 用于传输和存储数据,语法要求严格; 标签没有被预定义,需要在头信息中自行定义根标签.

  • 头信息解析
    在这里插入图片描述

  • Shift+Tab缩进调整格式

  • xml文件中的转义字符:
    在这里插入图片描述

2️⃣ HTML:用于显示数据
3️⃣单元测试框架(junit):可以在不使用main方法,不创建对象的情况下运行非静态方法

  • 原理:扫描整个类中所有带有@Test注解的方法通过反射创建该类的对象,通过对象调用公共方法.
  • 规则: 这个类中的方法满足以下四点,才能使用
    • 公共
    • 非静态
    • void
    • 无参
  • 不满足会报java.lang.exception:No tests found matching
  • @Before标记的方法会在@Test标记的方法之前执行

4️⃣maven坐标查询
mvnrepository.com

🔲 目标:
1、MyBatis快速入门

2、MyBatis对数据库中数据的增删改查操作

3、#{}占位符的应用

4、MyBatis的Mapper接口开发

5、MyBatis的注解开发

1.MyBatis简介

1.1什么是MyBatis

  • MyBatis是持久层(DAO)框架:用于连接并访问数据库
  • Mybatis封装了JDBC操作数据库的过程简化JDBC代码.
  • Mybatis通过xml或者注解方式将要执行的Sql语句进行配置,使用时加载.

对比:
JDBC: insert into emp value(null,?,?,?); java程序中接收参数再去插入参数

Mybatis:先写一个不带参数值的语句,再将参数值设置进来,将sql语句和参数对应起来,生成最终要执行的sql
在这里插入图片描述
(1)mybatis-config.xml是Mybatis的核心配置文件,通过其中的配置可以生成SqlSessionFactory,也就是SqlSession工厂

(2)基于SqlSessionFactory可以生成SqlSession对象

(3)SqlSession是一个既可以发送SQL去执行,并返回结果,类似于JDBC中的Connection对象,也是Mybatis中至关重要的一个对象。

(4)Executor是SqlSession底层的对象,用于执行SQL语句

(5)MapperStatement对象也是SqlSession底层的对象,用于接收输入映射(SQL语句中的参数),以及做输出映射(即将SQL查询的结果映射成相应的结果)

1.2为什么要使用MyBatis

  • 使用JDBC访问数据库:

1️⃣使用JDBC访问数据库有大量冗余代码(比如注册驱动、获取连接、获取传输器、释放资源等);

2️⃣JDBC自身没有连接池,会频繁的创建连接和关闭连接,效率低;

3️⃣SQL是写死在程序中,一旦修改SQL,需要对类重新编译(java->class)再打包部署发布到用户服务器;

项目中会将URL地址,用户名,密码,sql语句 等都放在配置文件中

4️⃣对查询SQL执行后返回的ResultSet对象,需要手动处理,有时会特别麻烦

  • 使用mybatis访问数据库:

1️⃣Mybatis对JDBC对了封装,可以简化JDBC代码;

2️⃣Mybatis自身支持连接池(也可以配置其他的连接池),因此可以提高程序的效率;

3️⃣Mybatis是将SQL配置在mapper文件中,修改SQL只是修改配置文件,类不需要重新编译。

4️⃣对查询SQL执行后返回的ResultSet对象,Mybatis会帮我们处理,转换成Java对象。


2.MyBatis框架搭建

2.1MyBatis快速入门

2.1.1 准备数据

创建yonghedb库、emp表,并插入若干条记录

-- 1、创建数据库 yonghedb 数据库
create database if not exists yonghedb charset utf8;
use yonghedb; -- 选择yonghedb数据库
-- 2、删除emp表(如果存在)
drop table if exists emp;
-- 3、在 yonghedb 库中创建 emp 表
create table emp(
  id int primary key auto_increment,
  name varchar(50),
  job varchar(50),
  salary double
);
-- 4、往 emp 表中, 插入若干条记录
insert into emp values(null, '王海涛', '程序员', 3300);
insert into emp values(null, '齐雷', '程序员', 2800);
insert into emp values(null, '刘沛霞', '程序员鼓励师', 2700);
insert into emp values(null, '陈子枢', '部门总监', 4200);
insert into emp values(null, '刘昱江', '程序员', 3000);
insert into emp values(null, '董长春', '程序员', 3500);
insert into emp values(null, '苍老师', '程序员', 3700);
insert into emp values(null, '韩少云', 'CEO', 5000);

2.1.2创建工程,准备开发环境

  • 创建Maven的java工程
    src/main/java目录下创建测试类

  • 在pom.xml文件中引入相关依赖:junit、mysql、mybaits、log4j

<dependencies>
    <!-- junit单元测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.9</version>
    </dependency>
    <!-- mysql驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.11</version>
    </dependency>
    <!-- mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.2.8</version>
    </dependency>
    <!-- 整合log4j -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.6.4</version>
    </dependency>
</dependencies>

2.1.3 提供mybatis-config.xml核心配置文件

  • 配置事务管理方式
  • 连接数据库的基本信息:驱动全类名,URL,用户名,密码
  • 是否使用连接池
  • 导入sql映射文件

configuration:MyBatis的全局配置标签,在xml头信息中定义的根标签, 包含environments标签和mappers标签

  • environments标签下配置开发环境
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

  • mappers标签导入sql映射文件(XXmapper.xml)
    在这里插入图片描述
    类目录:编译后的class文件,直接复制文件名

    在这里插入图片描述

    • 完整配置如下:
<?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">
<!-- MyBatis的全局配置文件 --> 
<configuration>
     <!-- 1.配置开发环境 -->
	<environments default="dev">
		<environment id="dev">
		<transactionManager type="JDBC">
		</transactionManager>
		<dataSource type="POOLED">
			<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
			<property name="url" 
					  value="jdbc:mysql:///yonghedb?charcterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai"/>
			<property name="username" value="root"/>
			<property name="password" value="root"/>
		</dataSource>
	  </environment>
   </environments>
	 <!-- 2.导入XxxMapper.xml文件-->
	<mappers>			
		<mapper resource="EmpMapper.xml"/>
	</mappers>
	
</configuration>

2.1.4提供EmpMapper.xml文件

  • 配置我们想执行的任何SQL语句(查询,新增,修改,删除等)

  • 通过namespace和id属性定位要执行的sql语句

  • 通过select insert update delete标签来存放要执行的sql语句,标签上可声明属性有id、resultType、resultMap

    1️⃣ id:

    • 要求值不能重复。常与方法名同,见名知义
    • 将来在执行SQL时,可以通过 【namespace + id】找到指定SQL并执行。

    2️⃣ resultType:

    • 控制查询SQL执行后返回值的类型或集合中的泛型,要求为全类名
    • 如查询emp表中的单条记录,返回值是一个Emp对象,因此,resultType=“com.tedu.pojo.Emp”;
    • 如果查询emp表中的多条记录,返回值是一个List,此时resultType的值应该集合中的泛型,因此resultType=“com.tedu.pojo.Emp”;

    3️⃣ resultMap:复杂对象结构(例如多表关联查询等)。 使用 resultType 或 resultMap,但不能同时使用。

–>

<!DOCTYPE mapper
	PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >


<!-- mapper:根标签.
     namespace属性:名称空间,也叫命名空间,要求不能重复。
     			   用于标识当前这个mapper文件,不写后缀
     			   往往需要读取文件信息是写后缀
     			   
	 在java程序中通过[namespace + id ]属性定位到要执行哪一条SQL语句-->
<mapper namespace="EmpMapper">
<!--练习一:查询emp表中得所有员工   -->
<select id="findAll" resultType="cn.tedu.pojo.Emp">
	select * from emp

</select>
</mapper>

2.1.5 提供POJO类

为了封装员工信息而提供的Emp类, 我们称之为实体类(简单java对象, POJO: Plain Ordinary Java Object),只用作封装信息,无任何业务
如果要查询所有的员工信息,员工信息查询出来后需要封装到Java对象中
因此这里需要提供的Emp(员工)类,这个类用于封装所有的员工信息

public class Emp {
		//提供私有属性
		private Integer id;
		private String name;
		private String job;
		private Double salary;

		//提供属性对应的get和set方法
		public Integer getId() {
			return id;
		}
		public void setId(Integer id) {
			this.id = id;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public String getJob() {
			return job;
		}
		public void setJob(String job) {
			this.job = job;
		}
		public Double getSalary() {
			return salary;
		}
		public void setSalary(Double salary) {
			this.salary = salary;
		}
		//toString方法
		@Override
		public String toString() {
			return "Emp [id=" + id + ", name=" + name + ", job=" + job + ", salary=" + salary + "]";
		}
	}

2.2Mybatis操作数据库实现

  • 1.读取mybatis核心配置文件中的配置信息
    在这里插入图片描述

  • 2.基于读取的配置信息获取工厂对象
    在这里插入图片描述

  • 3.通过工厂对象打开与数据库的连接
    在这里插入图片描述

  • 4通过namespace+id找到并执行sql语句
    在这里插入图片描述

  • 5.遍历list集合查看数据

  • 完整代码如下

package cn.tedu;
import cn.tedu.pojo.Emp;

public class MybatisTest01 {

	public static void main(String[] args) throws Exception {
	
	InputStream in= Resources.getResourceAsStream("mybatis-config.xml");
	
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	
	SqlSession session = factory.openSession();
	
	List<Emp> list = session.selectList("EmpMapper.findAll");
	
	for (Emp e : list) {
		System.out.println(e);
	}
  }
	
}

2.2.1 Mybatis封装数据的过程

  • 首先将数据封装为8个Emp对象,再将对象放入List集合中返回给查询者
    在这里插入图片描述

  • 如果emp表中的列名 和 Emp对象中的属性名不相同, 查询的结果能封装到Emp对象中吗?

emp表中的列名是:empnameEmp对象中的属性名是:name
生成set方法: setEmpnamesetName 方法不对应
暴力反射: empnamename 找不到同名属性

所以数据没法封装!


2.3 Mybatis程序中常见的问题:

找Cause

1️⃣Emp实体类中添加了有参构造函数,但没有添加无参构造函数!
Mybatis底层会基于反射创建Emp类的对象实例, 创建时使用的是Emp类的无参构造函数,不添加任何构造函数,默认存在无参构造.添加了有参构造,无参会被覆盖
Cause:在这里插入图片描述
Emp.() 表示无参构造函数


2️⃣ 执行SQL语句时, namespace或id写错了, 找不到要执行的SQL语句就会报错:

  • Cause: Mapped Statements collection does not contain value for EmpMapper.finaAll

  • 每条SQL语句都有对应的 namespace+id, 而这些信息是存放在一个map集合中

  • Mapped Statements collection用于存放sql语句以及它的定位标识(K)的map集合

KV
EmpMapper.findAllselect * from emp
  • 如果Mapper文件标签的id值重复汇报如下错误
    Cause:在这里插入图片描述

3️⃣ 连接数据库的配置信息写错, 会导致连接不上数据库:
在这里插入图片描述DataSourceException: Unknown DataSource property: usename
– 报了一个未知的数据源(连接池)属性: usename

2.4 Mybatis增删改数据

  • 获取session连接, 增删改不需要 resultType属性
SqlSession session = null;
	
/*@Before标记的方法会在每个@Test标记的方法之前执行*/
	@Before
	public  void beforeMethod() throws Exception {

//1.读取mybatis核心配置文件中的配置信息(起名:mybatis-config.xml)
		//xml:可扩展标示语言(Extensive Markup Language)
		//ml:markuplanguage 强标记语言,格式严格
InputStream in= Resources.getResourceAsStream("mybatis-config.xml");

//2.基于读取的配置信息获取SqlSessionFactory对象(工厂)
SqlSessionFactory factory = new  					                         SqlSessionFactoryBuilder().build(in);
        
//3.通过工厂对象打开与数据库的连接(即获取SqlSession对象,类似于connection)
        //提交方式二:true: 表示自动提交事务, 默认是false, 表示关闭自动提交, 需要手动提交!
        session = factory.openSession( true );
		
  • 新增
  • 未设置事务提交之前,sql语句生成的是临时数据,程序执行完成事务没有提交默认回滚,临时数据撤销,id=9被使用过,自增变量为保证id不重复,默认自增,id唯一即可,不必连续
    在这里插入图片描述

<insert id="insert">
	insert into emp value(null,'赵云','保安',6000)
</insert>
@Test
	public void testInsert() {
		int rows = session.insert("EmpMapper.insert");
		System.out.println("影响行数为"+rows);
		//事务提交方式一:手动提交
		session.commit();
	}
  • 修改
<!-- update标签也可执行新增和删除,但是注意id属性值不可重复-->
<update id="update">
	update emp set job='保安',salary=20000 where name='苍老师'
</update>
@Test
	public void testUpdate() {
		int rows = session.update("EmpMapper.update");
		System.out.println("影响行数为"+rows);
	}
  • 删除
<delete id="delete">
	delete from emp where name='陈子枢'
</delete>
@Test
	public void testUpdate() {
		int rows = session.update("EmpMapper.update");
		System.out.println("影响行数为"+rows);
	}

2.5启用log4j日志框架

log4j–log for java
mybatis默认已经支持了log4j框架,log4j是专门为Java提供的日志框架,用于打印程序日志信息

在mybatis中使用log4j时, 只需两步:

  • 导入log4j的jar包
  • 导入log4j的配置文件(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
-------------------------------------------------
# Global logging configuration
log4j.rootLogger=DEBUG, Console, LOGFILE
# Console output...
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%5p [%t] %d{yyyy-MM-dd hh:mm:ss} - %m%n

log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.file=./mylog.log
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%5p [%t] %d{yyyy-MM-dd hh:mm:ss} - %m%n

3.Mybatis中的占位符

  • 实际应用场景中,前端发送请求传递给服务器,服务器将请求参数传递给Mybatis处理,Mybatis实现与数据库的交互,请求参数值并不固定,所以需要占位符占位.

  • 在Mybatis框架中,大部分情况都是用#{}占位符

  • #{}占位符其实就是JDBC中的问号(?)占位符,是为SQL语句中的参数值进行占位。例如:

    • 查询:select * from emp where name = 参数值
    • 新增: insert into emp value(null, 参数值, 参数值, 参数值)
    • 修改: update emp set 列=参数值, 列=参数值, … where 列=参数值…
    • 删除: delete from emp where 列=参数值
  • mybatis中有两种占位符, 分别是: #{}, ${}
    其中最常用的是 #{} 占位符

3.1 #{}占位符的使用

3.1.1参数传递

#{}占位符在对前端传递来的参数进行拼接时,并不是简单将占位符替换为参数在进行参数拼接时

  • #{}占位符会在字符串或日期类型的参数两边加上单引号,做转义处理
  • 如果参数包含sql关键字(如连接词and or),怎当做普通文本处理
    在这里插入图片描述
  • 单独查询某些字段时,列名作为参数,此时#{}无法实现
    在这里插入图片描述

3.1.2 参数封装

#{}占位符: 其实就是JDBC中的问号(?)占位符在mybatis底层会将 #{}占位符翻译成问号 (?)

  • 如果在SQL语句中占位符只有一个, 占位符名称没有要求,但不能为空; 参数可以直接传递,不用封装;

  • 如果在SQL语句中的#{}占位符,不止一个,由于session连接操作数据库的方法限制,参数个数有限,需要将前端传过来的参数通过Map<>或者POJO对象进行封装;

    • 如果通过Map集合来封装SQL参数值,#{}占位符中名称要和Map集合的key保持一致!
      因为在mybatis底层是通过占位符名称作为key到map中获取对应的value
<!-- 练习05:新增员工信息: null, 马云, 教师, 800 -->
	<insert id="insert2">
		insert into emp value( null, #{name}, #{job}, #{salary} )
	</insert>
/* 练习05:新增员工信息: null, 马云, 教师, 800 */
	@Test
	public void testInsert2() {
		//声明一个map集合,将SQL参数封装到map中.,map中的key要和占位符名称保持一致,SQL参数由前端传递过来
		Map map = new HashMap();
		map.put( "name", "马云" );
		map.put( "job", "教师" );
		map.put( "salary", 800 );
		//根据namespace+id找到SQL语句, 将map作为参数传过去, 并执行SQL语句
		int rows = session.insert( "EmpMapper.insert2", map );
		System.out.println( "影响行数: "+rows );
	}
  • 如果通过POJO对象来封装SQL参数值,占位符名称要在POJO对象中有对应的getXxx方法,或者有对应的变量
    • 例如:#{job}占位符中的名称为job,那么就意味着,在Emp中要有getJob()方法或者有job变量,如果两者都有,会优先通过getXxx方法来获取POJO对象中存储的属性值,如果没有getXxx方法,会通过暴力反射直接获取Emp中job变量的值。
<!-- 练习06: 修改员工信息: 将马云的职位改为"CEO", 薪资改为80000 -->
	<update id="update2">
		update emp set job=#{job}, salary=#{salary}
		where name=#{name}
	</update>
@Test
	public void testUpdate3() {
		//将SQL语句中的参数封装到POJO对象中
		//Emp emp = new Emp(null, "马云", "教授", 8888.0);
		Emp emp = new Emp();
		emp.setName( "马云" );
		emp.setJob( "教授" );
		emp.setSalary( 8888.0 );
		//定位SQL并执行SQL
		session.update( "EmpMapper.update2", emp );
	}

3.2 ${}占位符

$ {}占位符:为SQL语句中的某一个SQL片段进行占位

3.2.1参数传递

参数传递时直接将$ {}占位符替换
直接拼接,可能会引发SQL注入攻击,因此不推荐大量使用!

3.2.1参数封装

  • 只有一个${}占位符,参数也必须得先封装到Map或者POJO对象中,再把Map或者POJO对象传递过去,规则和#{}占位符一致
  • mybatis底层在执行SQL语句时,使用的就是PreparedStatement对象来传输SQL语句!

	<!-- 练习09: 查询emp表中的所有员工信息, 动态显示要查询的列 -->
	<select id="findAll2" resultType="cn.tedu.pojo.Emp">
		select ${colName} from emp
	</select>

  • 只有一个 $ {}占位符也必须封装
/* 练习09: 查询emp表中的所有员工信息, 动态显示要查询的列
	 * select * from emp; "*"
	 * select id, name, job; "id,name,job"
	 */
	@Test
	public void testFindAll2() {
		Map map = new HashMap();
		map.put( "colName" ,  "name,job" );
		List<Emp> list = 
				session.selectList( "EmpMapper.findAll2", map );
		for (Emp emp : list) {
			System.out.println( emp );
		}
	}

3.2.3 SQL注入攻击

$ {}占位符,参数传递没有(?)占位,直接拼接在sql语句中
在参数传递时,添加条件字符,sql执行会发生改变
换成#{}占位符只会删除一条记录
在这里插入图片描述

4.Mybatis的动态sql

在员工列表页面中可以根据员工的薪资区间查询员工信息

  • 如果不传最低薪资 和 最高薪资, 默认查询所有薪资的员工信息
    select * from emp;
  • 如果传了一个最低薪资, 但没有传最高薪资:
    select * from emp where salary > 最低薪资
  • 如果传了一个最高薪资, 但没有传最低薪资:
    select * from emp where salary < 最高薪资
  • 如果既传了最低薪资, 也传了最高薪资:
    select * from emp where salary<最高薪资 and salary>最低薪资

前端传递的参数并不固定,实现单一功能的sql过于繁琐

动态SQL指的是SQL语句可以根据参数值的不同,来生成不同的SQL语句

4.1 if标签

  • test属性值为true(布尔表达式结果为true), 那if标签中的SQL片段就会参与整个SQL语句的执行,
  • 反之, 那么if标签的中内容将不会执行
		<if test="布尔表达式">
			SQL片段
		</if>
  • 根据员工的薪资区间查询员工信息
    两个条件都没传递,where连接词多余,需拼接条件,且不影响sql结果
	<select id="findBySal" resultType="cn.tedu.pojo.Emp">
		select * from emp
		where 1=1
			<if test="minSal != null">
				and salary > #{minSal} 
			</if>
			<if test="maxSal != null">
				and salary &lt; #{maxSal} 
			</if>
	</select>
	@Test
	public void testFindBySal() {
		Map map = new HashMap();
		map.put( "minSal" , 3000 );
		map.put( "maxSal" , 4500 );
		List<Emp> list = session.selectList( "EmpMapper.findBySal", map );
		for (Emp emp : list) {
			System.out.println( emp );
		}
	}

在这里插入图片描述

4.2 where标签

where标签中只要有任何一个条件成立, 就会生成where关键字,
并且如果标签内的条件前有多余连接词(比如or,and),where标签会将多余的连接词去掉

  • 优化:
<select id="findBySal2" resultType="cn.tedu.pojo.Emp">
		select * from emp
		<where>
			<if test="minSal != null">
				salary > #{minSal} 
			</if>
			<if test="maxSal != null">
				and salary &lt; #{maxSal} 
			</if>
		</where>
	</select>
@Test
	public void testFindBySal2() {
		Map map = new HashMap();
		//map.put( "minSal" , 3000 );
		map.put( "maxSal" , 4500 );
		
		List<Emp> list = session.selectList( "EmpMapper.findBySal2", map );
		for (Emp emp : list) {
			System.out.println( emp );
		}
	}

4.3 foreach标签

前端传递的参数类型为数组或集合时,参数个数不确定,需要动态参数sql,可使用foreach标签

foreach标签可以将前端传递数组或集合进行遍历,生成所需要的SQL片段

前端数据类型collection属性值
数组array
List集合list
map集合map集合的Key值

实质上mybatis会把前端传递的数组或list集合封装到map集合中,并指定key值为array/list
open属性:指定所生成SQL片段的起始符号,通常是左圆括号 (
close属性:指定所生成SQL片段的结束符号,通常是右圆括号 )
item属性:指定占位符中名称
separator属性:指定占位符中间的分隔符, 通常是逗号 ,

  • 根据员工的id批量删除员工信息
	 -->
	<delete id="deleteByIds">
		delete from emp where id in
		<foreach collection="list" open="(" item="id" separator="," close=")">
			#{id}
		</foreach>
	</delete>
@Test
	public void testDeleteByIds() {
		//int[] ids = {1,3,5,7}; 
		List list = new ArrayList();
		list.add( 2 );
		list.add( 4 );
		list.add( 6 );
		list.add( 8 );
		session.delete( "EmpMapper.deleteByIds", list );
	}

在这里插入图片描述

  • 根据员工的id批量更新员工信息
<update id="updateByIds">
		update emp set salary=salary + #{money}
		where id in
		<foreach collection="arrayIds" open="(" item="id" separator="," close=")">
			#{id}
		</foreach>
	</update>
@Test
	public void testUpdateByIds() {
		int[] ids = {2,4,6,8}; //要更新的员工的id数组
		Double money = 1000.0; //要涨的薪资
		Map map = new HashMap();
		map.put( "arrayIds" , ids );
		map.put( "money" , 1000 );
		session.update( "EmpMapper.updateByIds", map );
	}

5. Mybatis的Mapper接口开发

5.1为什么使用接口开发

  • sqlsession 定位标识写错并不会检查到.
  • sqlsession执行sql语句的方法可传参数个数有限,需要封装前端传递来的参数,较为繁琐
  • 接口开发是直接调用方法,方法在编译期就会检查,且添加中间层(接口),sqlsession只要获取接口子类对象,对象中包含执行方法,避免多个参数出错,使mybatis和spring整合时更为简化

5.2 mapper接口开发四个规则(开发步骤):

  • 接口全类名(包名.接口名)与对应的映射文件的namespace值相同
  • 接口中的方法名与 sql标签的id值对
  • 接口方法的返回值类型与sql标签的resultType值对应
  • 接口方法的参数与占位符对应
返回值类型resultType值
实体对象(Emp)cn.tedu.pojo.Emp
集合(List < Emp>)指定集合中的泛型

在这里插入图片描述

EmpMapper.xml文件配置操作员工表的sql EmpMapper接口规定增删改查数据库的操作方法 Mybatis框架去操作数据库

  • 案例1: 查询emp表中的所有员工信息
  • xml配置
<mapper namespace="cn.tedu.dao.EmpMapper">

	<select id="findAll" resultType="cn.tedu.pojo.Emp">
			select * from emp
	</select>
	
</mapper>
  • 接口
public interface EmpMapper {
	@Select("select *from emp")
	public List<Emp> findAll();
}
  • 测试类
    Mybatis会根据接口,提供接口实现类,实现父接口的方法 ,还会根据子类创建一个实例
    实现类做的事实质上是Mybatis根据想办法获取定位标识(接口全类名.接口中的方法)帮我们找到要执行的sql
public class MybatisTest02 {
	
	SqlSession session = null;
	
	@Before
	public  void beforeMethod() throws Exception {
	InputStream in= Resources.getResourceAsStream("mybatis-config.xml");	
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	session = factory.openSession(true);
  }
	
	@Test
	public void testFindAll() {
		//获取由Mybatis提供的接口的子类对象实例(参数为接口的字节码对象),底层利用反射剖析这个接口
		EmpMapper mapper = session.getMapper(EmpMapper.class);
		//接口的实现类中实现了定义的查询方法
		List<Emp> list = mapper.findAll();
		for (Emp emp : list) {
			System.out.println(emp);
		}
	}
  • 模拟mybatis提供的EmpMapper接口的实现类
  • API
    • Object
      在这里插入图片描述

    • Class extences Object
      在这里插入图片描述
      在这里插入图片描述

    • Thread extends Object
      在这里插入图片描述

在这里插入图片描述

	public class EmpMapperImpl implements EmpMapper{
	
		//当外部有需求与实现类提供的功能一样,就会去new实现类对象
		//new实现类对象时,将session对象作为参数传递进来,
		//保存在实现类对象内部,类中就可以使用session对象提供的方法
		public EmpMapperImpl( SqlSession session ) {
			this.session = session;
		}
		
		private SqlSession session;
		
		/* 查询所有的员工信息 */
		public List<Emp> findAll() {
			//获取当前这个类的父接口的全类名(=namespace)
			1.通过当前类对象(this)获取当前实现类的字节码对象
			2.获取其所有父接口的字节码对象
			   getInterclass方法返回一个所有父接口字节码对象组成的数组
			3.根据数组下标获取父接口全类名,即为namespace
			//getName获取全类名
			//getSimpleName获取简单名称,不带包名
			String interName = this.getClass().getInterfaces()[0].getName();
			//获取当前方法的名字(=SQL标签的id值)
			1获取当前线程
			2.getStackTrace() 返回方法调用栈的信息组成的数组
			//finAll()内调用的getStackTrace(),
			//栈内存是先进后出,findAll()在栈底
			//获取方法名时,数组下标为1
			//每次调用方法,都会在栈内存中开辟一块方法栈空间
			//方法执行完成后,方法栈占用的内存释放
			StackTraceElement[] st = Thread.currentThread().getStackTrace();
			//获取当前方法的名字(=SQL标签的id值)
			String methodName = st[1].getMethodName();
			List<Emp> list = session.selectList( interName+"."+methodName );
			
		
			/*for (StackTraceElement stackTraceElement : st) {
				System.out.println( stackTraceElement );
			}
			System.out.println("下面获取方法调用栈数组中第二个元素中的所有信息: ");
			System.out.println( "获取当前方法的类名:"+st[1].getClassName() );
			System.out.println( "获取当前方法的名字:"+st[1].getMethodName() );
		    System.out.println( "获取当前方法所属的文件名:"+st[1].getFileName() );
			System.out.println( "获取当前方法调用的行数:"+st[1].getLineNumber() );
			*/
			return list;
		}
	}	
  • 测试自己提供的EmpMapper接口的实现类
  • 方法栈信息:
    栈内存,先进后出,所以实现类方法总是在第二的位置.
    在这里插入图片描述
	@Test
	public void testFindAll() {
		//获取EmpMapper接口的子类的对象实例
		EmpMapper mapper = new EmpMapperImpl(session);
		List<Emp> list = mapper.findAll();
		for (Emp emp : list) {
			System.out.println( emp );
		}
	}
  • 案例2 带占位符的新增
    新增的员工信息是从前端传过来的
<insert id="insert2">
		insert into emp value( null, #{name}, #{job}, #{salary} )
	</insert>
void insert2(Map map);
@Test
	public void testInsert2() {
		EmpMapper mapper = session.getMapper(EmpMapper.class);
			Map map =new HashMap();
			map.put("name","马云");
			map.put("job","禽兽");
			map.put("salary","80000");
		mapper.insert2(map);
	}
  • 案例3:带占位符的更新

5.Mybatis的注解开发

使用xml方式和注解开发的区别?

  • 使用xml方式配置SQL语句,写起来相比注解要麻烦一些; 而注解方式,不用写配置文件,直接在接口的方法上面添加一个注解(@Select,@Insert,@Update,@Delete…),将SQL语句直接写在注解的括号里即可;
  • 使用注解方式配置SQL语句又回到将SQL语句写在Java程序中,如果将来SQL语句一旦发生变化,就意味着要修改java源文件(.java),改完后要重新编译整个项目,再打包、部署到服务器上,相比来说较为麻烦。

5.1开发步骤

1.由于使用注解开发,要将SQL语句配置在注解中,因此EmpMapper.xml文件也就不需要了,在mybatis-config.xml文件中也就不需要引入EmpMapper.xml文件了
在mybatis-config.xml文件中引入SQL语句所在的mapper接口,因为SQL语句存放在接口中

	<mappers>
		<package name="cn.tedu.dao"/>
	</mappers>

2.将SQL语句移动到EmpMapper.java接口中

public interface EmpMapper {
		
		/* 练习01: 查询emp表中的所有员工信息 */
		@Select("select id,name,job from emp")
		public List<Emp> findAll();
		
		/* 练习02: 新增员工信息(没有占位符) */
		@Insert("insert into emp value(null, '赵云云', '高级Java工程师', 35000)")
		public void insert();
		
		/* 练习05:新增员工信息: null, 马云, 教师, 800(有占位符) */
		@Insert("insert into emp value(null, #{name}, #{job}, #{salary})")
		public void insert2(Map map);
		
		/* 练习06: 修改员工信息: 将马云的职位改为"CEO", 薪资改为80000(有占位符) */
		@Update("update emp set job=#{job}, salary=#{salary} where name=#{name}")
		public void update2(Emp emp);
		
	}

4.测试(由于这里只是将SQL语句从XxxMapper.xml文件中移动到了XxxMapper接口中, 所以测试代码直接使用之前的即可!)


6. tomcat服务器

一、服务器相关概念
1、什么是服务器
服务器:分为软件服务器和硬件服务器
硬件服务器:运行在互联网上的、具有静态IP的一台计算机(通常配置比较高)
软件服务器:运行在互联网上的计算机程序(软件),将服务器软件安装在硬件服务器上,才可以对外提供服务。
服务器软件分为很多种:数据库服务器(MySQL,Oracle,SQL Server等),Web服务器(tomcat,jetty,jboss等),邮件服务器,FTP服务器

2、什么Web服务器
Web服务器: 运行在互联网上的计算机程序,专门用于接收客户端(主要指浏览器)的请求,根据请求进行处理,最后给出回应!
比如:打开浏览器,输入"http://www.baidu.com"回车,其实访问的就是百度的服务器,此时会向百度服务器发送一个请求,请求百度的首页,百度服务器会接收并处理这个请求,根据请求给出回应(将百度首页响应给客户端浏览器)

tomcat就是一个Web服务器,特点是:小巧灵活,简单易用,学习成本非常低!

二、Tomcat服务器下载、安装、启动、配置
1、下载tomcat服务器
下载地址: http://tomcat.apache.org
tomcat分为很多版本,有windows版本(解压版和安装版)、linux版本
推荐使用解压版(需要用的时候解压一份,不需要用了直接删除解压的目录即可!)

2、tomcat的安装、启动、配置
2.1.安装tomcat
安装:解压之后就可以使用(安装的路径中最好不要包含中文和空格)

在启动tomcat之前,需要配置(检查)一个JAVA_HOME环境变量,该变量需要指向JDK的安装根目录
变量名:JAVA_HOME
变量值:D:\software\Java\jdk1.8.0_161 (此处换成你安装的JDK的根目录)
由于tomcat服务器是由Java语言开发的,所以运行tomcat需要JDK的支持
JAVA_HOME这个变量就是在告诉tomcat服务器,需要使用哪一个位置上的JDK.

2.1.启动tomcat
启动tomcat: 通过[tomcat安装目录]/bin/startup.bat文件可以启动tomcat服务器;
关闭tomcat: 通过[tomcat安装目录]/bin/shutdown.bat文件可以关闭tomcat服务器;
或者直接点击右上角的叉号

启动tomcat之后,可以打开浏览器,访问: 
	http://localhost:8080
	http://127.0.0.1:8080
	
如果可以访问到tomcat服务器的主页,就说明tomcat安装并且启动成功了!

3.修改tomcat服务器默认端口
如果不修改端口,每次在访问tomcat服务器时,都需要在[主机名/ip地址]的后面加上:8080
如果想在访问时,在主机名或ip地址后面省略端口,可以将端口修改为80(这个端口特殊,可以省略不写!)

修改端口的方法是:
	找到[tomcat安装目录]/conf/server.xml并用文本编辑工具打开这个文件
	找到文件的69行,将Connector标签上的port属性值改为80,保存文件,并重启服务器即可生效!
重启服务器后,就可以通过如下路径访问tomcat服务器:
	http://localhost:80
	http://localhost
	http://127.0.0.1:80
	http://127.0.0.1

三、tomcat服务器的目录结构(了解)
bin: 存放批处理文件的目录(startup.bat、shutdown.bat文件)
conf: 存放tomcat配置文件的目录(server.xml是tomcat核心配置文件)
lib: 存放tomcat服务器在运行时所依赖的jar包的目录
logs: 存在tomcat服务器在运行时产生的日志文件的目录
temp: 存放tomcat服务器在运行时产生的临时文件的目录

work: 存放tomcat服务器在运行期间产生的一些工作文件
	(JSP在第一次被访问时翻译后的Servlet文件、
	session对象序列化后产生的文件等都会放在这个目录下)
webapps: 是Web应用的存放目录,放在这个目录中的Web应用程序,
	可以通过localhost虚拟主机进行访问
	webapps目录是localhost主机默认存放Web应用的目录
	把Web应用放在webapps目录下,就相当于发布到了localhost主机中

四、Web应用和虚拟主机
在这里插入图片描述

1、Web应用:
Web应用其实就是一个目录,其中可以包含很多资源文件(html/css/js/图片/jsp/servlet…等)

虚拟主机中不能直接管理Web资源文件(html/css/js/图片/jsp…等)
需要将Web资源文件组织成一个Web应用(目录),将Web应用发布到虚拟主机中运行才可以被虚拟主机所管理

2、虚拟主机:
就是在tomcat服务器中配置的一个站点,在访问时就好像在访问一台真实独立的主机一样
我们将这个站点称之为是,运行在tomcat服务器中的一台虚拟主机
tomcat服务器中可以配置多个站点,每一个站点都是一台虚拟主机。
下面是tomcat默认提供的localhost主机的配置:

<Host name="localhost" appBase="webapps"...></Host>
<Host name="www.baidu123.com" appBase="baidu"...></Host>
1)在服务器硬件上安装了一个tomcat服务器软件
2)在tomcat服务器软件内部可以配置多个站点(虚拟主机),其中tomcat默认自带了一个localhost虚拟主机。
3)localhost虚拟主机默认管理Web应用的目录--webapps,发布到webapps目录下的web应用,也就都发布到了localhost主机中
4)往webapps中发布了一个 jt web应用,其中包含一些Web资源文件
5)web资源文件可以是(html/css/js/图片/servlet/jsp等)

五、Web应用
1、Web应用的目录结构
news(目录,Web应用)
|-- 也可以将Web资源文件放在Web应用的根目录下
|-- 其它目录(放在其它目录中的资源文件可以被浏览器直接访问到)
|-- WEB-INF目录(隐私目录,放在这里面的资源文件,不能被浏览器直接访问)
|-- classes目录(Java程序编译后的class文件会放在这个目录下)
|-- lib目录(Web应用所依赖的jar包会放在这个目录下)
|-- web.xml文件(当前Web应用的核心配置文件)

2、如何发布一个Web应用到虚拟主机中
直接将Web应用的目录复制到虚拟主机所管理的目录下即可
例如:将news复制到webapps目录下,由于webapps是localhost主机发布web应用的目录,
所以相当于将news发布到了localhost主机中,可以通过localhost主机进行访问!


7. Servlet

一、什么是Servlet
Servlet 是由SUN公司提供的一门Web资源开发技术(规范,接口)
Servlet是本质上是一个Java程序,但和我们之前接触的Java程序不同的是,
Servlet无法独立运行(Servlet中没有main函数)
需要将Servlet程序放在服务器中,由服务器调用才可以执行!

运行在服务器中的Servlet程序作用是: 对服务器接收的请求进行处理(处理请求)
SpringMVC的底层是servlet
Mybatis的底层是JDBC
Spring框架负责SpringMVC,Mybatis中的一些对象的创建

浏览器通过流给服务器发数据
服务器处理完请求也是通过流给浏览器发数据

在这里插入图片描述

  • 浏览器发出请求,服务器负责接受请求,对请求处理后,将请求交给Servlet
  • Servlet负责处理服务器(Tomcat)服务器发过来的请求
  • 如果需要查询数据,服务器会连接数据库返回查询结果给Servlet
  • jsp文件会由servlte编译成html文件
  • 浏览器的作用就是解析html文件渲染至页面

Servlet3.1的jar包
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>

Servlet2.5的jar包
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    <scope>provided</scope>
</dependency>

二、开发Servlet程序

  • Servlet接口

    • GenericServlet 实现了Servlet接口,并实现了其中的方法

      • HttpServlet 继承了GenericServlet,也实现了其中的方法

      在开发时,我们只需要继承HttpServlet,并继承其中的方法即可!
      1、开发Servlet程序的步骤
      1)写一个类,需要实现一个Servlet接口或者继承Servlet接口的子类
      2)在web.xml文件中配置Servlet对外访问的路径,再将Web应用发布到服务器即可!

    如果是Servlet3.0及以上版本, 可以使用注解方式配置Servlet访问路径

4、Servlet在web.xml文件中的配置
全限定类名(全类名): 包名.类名 或者 包名.接口名

<servlet>	
	<servlet-name>HelloWorld</servlet-name>
	<servlet-class>cn.tedu.HelloWorld</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>HelloWorld</servlet-name>
	<url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>
1)每创建一个Servlet,eclipse会帮我们生成至少8行配置信息
	这8行配置信息由一个Servlet标签和一个servlet-mapping标签组成
	这两个标签中的<servlet-name>标签中的内容一致,决定了它俩是一组配置
2)<servlet-class>标签中配置的当前Servlet类的全类名(包名.类名)
	将来服务器根据访问路径找到这个全类名,再利用反射+全类名可以获取当前Servlet类的实例	
3)<url-pattern>标签中配置了外界该通过什么路径来访问当前Servlet。
	也就是说,这里配置什么路径,外界就得通过什么路径来访问这个Servlet!

注意事项1: 如果不知道什么原因,tomcat服务器启动失败了,可以将Eclipse创建的Server删除,
	再重新创建一份(删除Server同时,也将左侧的Servers项目从工作空间中删除!)

注意事项2: 在将tomcat和Eclipse整合之后,tomcat默认开启了热部署功能:
	在修改了代码后,不用重新发布,也不需要重启服务器,就可以运行最新的效果!
	(如果是创建了新的Servlet类,或者修改了web.xml文件,则需要重启服务器,才会生效!)

5、创建Servlet3.0或以上版本的项目
在Servlet3.0的项目中, 可以通过注解方式配置Servlet相关信息
------------------------------------------
@WebServlet("/HelloWorld")
public class HelloWorld extends HttpServlet{}
------------------------------------------
在@WebServlet这个注解内部所配置的内容就是(xml方式)url-pattern中配置的访问路径
服务器通过扫描注解定位到当前这个Servlet,获取该类的全路径,
通过全路径从硬盘上加载这个类到内存中,获取该类的字节码对象,再利用反射+字节码对象创建该类的实例,然后通过HelloWorld类的对象实例再调用其中的方法!

@WebServlet("/HelloWorld")
public class HelloWorld extends HttpServlet {
	private static final long serialVersionUID = 1L;
	//Http: 提交方式GET和POST对应以下两种方法,
	  
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//设置服务器发送数据以及浏览器接收数据的编码
		response.setContentType( "text/html;charset=utf-8" );
		//向浏览器响应数据
		response.getWriter().write( "世界, 你好!" );
	}
	//但是无论gei还是post 最后都会执行doGet
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值