一、MyBatis简介

0、MyBatis官方中文文档

1、MyBatis

  • 和数据库进行交互
  • 持久化层框架(SQL映射框架)
  • 半自动框架
  • 前身是iBatis

2、持久化层技术

  • 原始的JDBC–>dbutils(QueryRunner)–>JdbcTemplate–>等等

称为工具

  • 一些功能的简单封装

框架

  • 某些领域的整体解决方案
    • 要考虑缓存,异常处理问题,部分字段映射问题等

不用原生JDBC原因

  • 麻烦(使用流程)

    image-20201214103213154


  • sql语句是硬编码在程序中的

    • 耦合(数据库层和java编码耦合)

3、Hibernate

  • 数据库库交互的框架(ORM框架)
    • ORM(Object Relation Mapping)对象关系映射
    • 全自动的框架

1.优点

  • 方便

image-20201214104326397


2.缺点

  1. 无法定制sql(二八理论)
  2. 要学HQL(Hibernate Query Language)(反模式)
  3. 全映射框架(做部分字段映射很难)

希望

  • 最好有一个框架
    • 支持定制化sql
    • 功能强大
    • sql也不要硬编码在java文件中(导致维护修改起来比较麻烦)

4、MyBatis应运而生

  1. MyBatis将重要的步骤抽取出来可以人工定制,其他步骤自动化

    image-20201214110226659


  2. 重要步骤都是写在配置文件中(好维护)

  3. 完全解决数据库的优化问题

  4. MyBatis底层就是对原生JDBC的一个简单封装

  5. 既将java编码与sql抽取了出来,还不会失去自动化功能(半自动化的持久化层框架

  6. mybatis是一个轻量级的框架


二、HelloWorld

1、环境搭建

1.创建一个java工程

2.创建测试库

  • 测试表,封装数据的javaBean和操作数据库的dao接口

3.用MyBatis操作数据库

1、导包(建议导入日志包)

mybatis-3.5.6.jar

mysql-connector-java-8.0.22.jar

log4j-1.2.17.jar(可选)

  • log4j(日志框架):依赖类路径下一个log4j.xml配置文件

2、写配置

  1. 第一个配置文件(==全局配置文件==)(mybatis-config.xml

    • ==指导mybatis==正确==运行==的一些全局设置(比如连接哪个数据库)
  2. 第二个配置文件(==SQL映射文件==)(EmployeeDao.xml

    • 编写每一个方法如何向数据库发送sql语句,如何执行(相当于==对Dao接口的一个实现描述==)

      1. mappernamespace属性改为接口的全类名

      2. 配置细节

        <!-- namespace:名称空间,写接口的全类名;相当于告诉MyBatis这个配置文件是实现哪个接口的 -->
        <mapper namespace="com.lql.dao.EmployeeDao">
                
            <!-- public Employee getEmpById(Integer id); -->
            <!-- select:用来定义一个查询操作;
            id:方法名,相当于这个配置是对于某个方法的实现
            resultType:指定方法运行后的返回值类型(查询操作必须指定的)
            #{属性名}:代表取出传递过来的某个参数的值
             -->
          <select id="getEmpById" resultType="com.lql.bean.Employee">
            select * from employee where id = #{id}
          </select>
        </mapper>
        
      3. 我们写的dao接口的实现文件,mybatis默认是不知道的,需要在全局配置文件中注册

        <!-- 引入我们自己编写的每一个接口的实现文件 -->
        <mappers>
            <!-- resource:表示从类路径下找资源 -->
            <mapper resource="mybatis/EmployeeDao.xml"/>
        </mappers>
        

3、测试

  1. 根据全局配置文件先创建一个SqlSessionFactory

    //1、根据全局配置文件创建出一个SqlSessionFactory
    //SqlSessionFactory:是SqlSession工厂,负责创建SqlSession对象
    //SqlSession:sql会话(代表和数据库的一次会话)
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    

  2. sqlSessionFactory中获取sqlSession对象操作数据库即可

    //2、获取和数据库的一次会话:getConnection();
    SqlSession openSession = sqlSessionFactory.openSession();
    //3、使用SqlSession操作数据库,先获取到dao接口的实现
    EmployeeDao employeeDao = openSession.getMapper(EmployeeDao.class);
    //4、调用之前的方法
    Employee employee = employeeDao.getEmpById(1);
    System.out.println(employee);
    

  3. ==注意==:测试可能会出现时区错误,是数据库版本的问题,可参考解决办法


4、总体架构:

image-20201220213612329


5、mybatis-config.xml

<?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/test?serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
      </dataSource>
    </environment>
  </environments>
  
  <!-- 引入我们自己编写的每一个接口的实现文件 -->
  <mappers>
  	<!-- resource:表示从类路径下找资源 -->
    <mapper resource="mybatis/EmployeeDao.xml"/>
  </mappers>
</configuration>

6、log4j.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
 
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
 
 <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
   <param name="Encoding" value="UTF-8" />
   <layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m  (%F:%L) \n" />
   </layout>
 </appender>
 <logger name="java.sql">
   <level value="debug" />
 </logger>
 <logger name="org.apache.ibatis">
   <level value="info" />
 </logger>
 <root>
   <level value="debug" />
   <appender-ref ref="STDOUT" />
 </root>
</log4j:configuration>

7、EmployeeDao.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">
<!-- namespace:名称空间,写接口的全类名;相当于告诉MyBatis这个配置文件是实现哪个接口的 -->
<mapper namespace="com.lql.dao.EmployeeDao">

	<!-- public Employee getEmpById(Integer id); -->
	<!-- select:用来定义一个查询操作;
	id:方法名,相当于这个配置是对于某个方法的实现
	resultType:指定方法运行后的返回值类型(查询操作必须指定的)
	#{属性名}:代表取出传递过来的某个参数的值
	 -->
  <select id="getEmpById" resultType="com.lql.bean.Employee">
    select * from employee where emp_id = #{emp_id}
  </select>
</mapper>

8、MyBatisTest.java

package com.lql.test;

import java.io.IOException;
import java.io.InputStream;

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.junit.Test;

import com.lql.bean.Employee;
import com.lql.dao.EmployeeDao;

public class MyBatisTest {

	@Test
	public void test() throws IOException {
		//1、根据全局配置文件创建出一个SqlSessionFactory
		//SqlSessionFactory:是SqlSession工厂,负责创建SqlSession对象
		//SqlSession:sql会话(代表和数据库的一次会话)
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		
		//4、调用之前的方法
		Employee employee;
		//2、获取和数据库的一次会话:getConnection();
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			
			//3、使用SqlSession操作数据库,先获取到dao接口的实现
			EmployeeDao employeeDao = openSession.getMapper(EmployeeDao.class);
			employee = employeeDao.getEmpById(1);
		} finally {
			openSession.close();
		}
		System.out.println(employee);
		
	}

}


9、EmployeeDao.java

package com.lql.dao;

import com.lql.bean.Employee;

public interface EmployeeDao {

	//数据库驱动
	//mysql-connector-java-8.0.22.jar
	//mybatis-3.5.6.jar
	//log4j-1.2.17.jar
	//按照员工id查询员工
	public Employee getEmpById(Integer id);
}


10、Employee.java

package com.lql.bean;

public class Employee {
	
	private Integer emp_id;
	
	private String emp_name;
	
	private Integer emp_gender;
	
	private String emp_email;

	public Integer getEmp_id() {
		return emp_id;
	}

	public void setEmp_id(Integer emp_id) {
		this.emp_id = emp_id;
	}

	public String getEmp_name() {
		return emp_name;
	}

	public void setEmp_name(String emp_name) {
		this.emp_name = emp_name;
	}

	public Integer getEmp_gender() {
		return emp_gender;
	}

	public void setEmp_gender(Integer emp_gender) {
		this.emp_gender = emp_gender;
	}

	public String getEmp_email() {
		return emp_email;
	}

	public void setEmp_email(String emp_email) {
		this.emp_email = emp_email;
	}

	@Override
	public String toString() {
		return "Employee [emp_id=" + emp_id + ", emp_name=" + emp_name + ", emp_gender=" + emp_gender + ", emp_email="
				+ emp_email + "]";
	}

	
}



2、如何写xml有提示

让eclipse找到dtd约束文件的位置

  • dtd约束文件mybatis jar包中解压复制出来)

    image-20201220212420081


  • 绑定约束文件的位置

  1. 复制dtd的引用网址
  2. eclipse进入window–》perferences–》XML–》XML Catalog
  3. 点击Add,Location写dtd约束文件的位置,Key type 选URI,Key填复制的引用网址
  4. 点击🆗,完事。

image-20201215084850196


3、细节

1.获取到的是接口的代理对象

  • mybatis自动创建的

2.SqlSessionFactory和SqlSession

  • SqlSessionFactory创建SqlSession对象,Factory只new一次就行
  • SqlSession相当于connection和数据库进行交互
    • 和数据库的一次会话
    • 应该创建一个新的sqlSession

三、全局配置文件

1、mybatis-config.xml

2、配置文档的顶层结构

  • configuration(配置)

    • properties(属性)
    • settings(设置)
    • typeAliases(类型别名)
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境配置)
      • environment(环境变量)
        • transactionManager(事务管理器)
        • dataSource(数据源)
    • databaseIdProvider(数据库厂商标识)
    • mappers(映射器)
  • 建议参考MyBatis官方中文文档


3、原生jdbc执行sql

  • 或者封装结果集
Connection connection = dataSource.getConnection();

String sql = "insert into employee(emp_name,emp_email,emp_gender) values(?,?,?)";

PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(0,"admin");
ps.setString(1,"aaa@qq.com");
ps.setInt(2,1);
int i = ps.executeUpdate();
//ps.close();connection.close();


4、增删改查测试

1.EmployeeDao.java

package com.lql.dao;

import com.lql.bean.Employee;

public interface EmployeeDao {

	public Employee getEmpById(Integer emp_id);
	public int updateEmployee(Employee employee);
	public boolean deleteEmployee(Integer emp_id);
	public int insertEmployee(Employee employee);
}


2.EmployeeDao.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 namespace="com.lql.dao.EmployeeDao">
  <!-- public Employee getEmpById(Integer emp_id);参数类型不用写 -->
  <select id="getEmpById" resultType="com.lql.bean.Employee">
  		<!-- sql语句不要写分号 -->
  		select * from employee where emp_id=#{emp_id}
  </select>
  
<!-- public int updateEmployee(Employee employee);
增删改不用写返回值类型;增删改是返回影响多少行
mybatis自动判断,如果是数字(int,long)
如果是boolean(影响0行自动封装false,否则true)
#{属性名}:从传入的参数对象中取出对应属性的值
-->
<update id="updateEmployee">
	UPDATE employee 
	SET emp_name=#{emp_name},emp_gender=#{emp_gender},emp_email=#{emp_email} 
	WHERE emp_id=#{emp_id}
</update>

<!-- public int deleteEmployee(Integer emp_id); -->
<delete id="deleteEmployee">
	DELETE FROM employee WHERE emp_id=#{emp_id}
</delete>

<!-- public int insertEmployee(Employee employee);-->
<insert id="insertEmployee">
	INSERT INTO employee(emp_name,emp_gender,emp_email) 
	VALUES(#{emp_name},#{emp_gender},#{emp_email})
</insert>

  </mapper>

3.MyBatisCRUDTest.java

package com.lql.test;

import java.io.IOException;
import java.io.InputStream;

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.junit.Before;
import org.junit.Test;

import com.lql.bean.Employee;
import com.lql.dao.EmployeeDao;

public class MyBatisCRUDTest {
	
	//工厂一个
	SqlSessionFactory sqlSessionFactory;
	
	@Before
	public void initSqlSessionFactory() throws IOException{
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}

	/**
	 * 测试添加
	 */
	@Test
	public void testInsert(){
		//1、获取和数据库的一次会话
		//true:代表自动提交
		SqlSession openSession = sqlSessionFactory.openSession(true);
		try {
			//2、获取到接口的映射器
			EmployeeDao mapper = openSession.getMapper(EmployeeDao.class);
			//3、测试
			int i = mapper.insertEmployee(new Employee(null, "tomcat", 0, "tomcat@qq.com"));
			System.out.println(i);
		} finally {
			//手动提交
			//openSession.commit();
			openSession.close();
		}
	}
	
	/**
	 * 测试删除
	 */
	@Test
	public void testDelete(){
		//1、获取和数据库的一次会话
		//true:代表自动提交
		SqlSession openSession = sqlSessionFactory.openSession(true);
		try {
			//2、获取到接口的映射器
			EmployeeDao mapper = openSession.getMapper(EmployeeDao.class);
			//3、测试
			mapper.deleteEmployee(4);
		} finally {
			//手动提交
			//openSession.commit();
			openSession.close();
		}
	}
	
	/**
	 * 测试修改
	 */
	@Test
	public void testUpdate(){
		//1、获取和数据库的一次会话
		//true:代表自动提交
		SqlSession openSession = sqlSessionFactory.openSession(true);
		try {
			//2、获取到接口的映射器
			EmployeeDao mapper = openSession.getMapper(EmployeeDao.class);
			//3、测试
			int i = mapper.updateEmployee(new Employee(5, "tom", 1, "tom@qq.com"));
			System.out.println(i);
		} finally {
			//手动提交
			//openSession.commit();
			openSession.close();
		}
	}
	
	/**
	 * 测试查询
	 * @throws IOException
	 */
	@Test
	public void test() throws IOException {
		//1、根据全局配置文件得到SqlSessionFactory对象
		//initSqlSessionFactory();
		//2、得到SqlSession对象;
		
		SqlSession openSession = sqlSessionFactory.openSession();
		
		//3、获取到dao接口的实现(映射器)
		EmployeeDao dao = openSession.getMapper(EmployeeDao.class);
		//class com.sun.proxy.$Proxy7
		System.out.println(dao.getClass());
		try {
			Employee employee = dao.getEmpById(1);
			System.out.println(employee);
		} finally {
			openSession.close();
		}
	}

}

5、MyBatis_config工程

  • ==注解配置配合使用==(重要的dao写配置,简单的dao就直接标注解

1.总体架构:

image-20201220225300510


2.mybatis-config.xml

<?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>

<!-- 1、和Spring的context:property-placeholder(引用配置文件)相同 -->
	<!-- 
	resource:从类路径下开始引用
	url:引用磁盘路径或者网络路径的资源
 	-->
	<properties resource="dbconfig.properties">
		<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
	</properties>
	
	<!-- 2、settings是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。  -->
	<settings>
		<!-- name:配置项的key,value:配置项的值 -->
		<!-- loginAccount ==》login_account -->
		<setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>
	
	<!-- 3、类型别名,为常用的类型(javaBean)起别名 -->
	<typeAliases>
		<!-- typeAlias:就是为一个JavaBean起别名,别名默认就是类名(不区分大小写),配置文件中就可以用别名了 -->
		<!-- alias="":指定一个别名 -->
		<!-- <typeAlias type="com.lql.bean.Employee" alias="emp"/> -->
		
		<!-- 批量起别名;name=""指定包名,默认别名就是类名 -->
		<!-- <package name="com.lql.bean"/> -->
		<!-- 我们推荐就用全类名 -->
	</typeAliases>
	
	<!-- 4、类型处理器,作用: -->
	<!-- <typeHandlers>
		自定义好的类型处理器就这么配置上就行了
		<typeHandler handler=""/>
	</typeHandlers> -->
	
	<!-- 5、插件是mybatis中的一个强大功能 -->

	<!-- 6、environments配置环境“们”
		environment:配置一个具体的环境,都需要一个事务管理器和一个数据源
			transactionManger
		后来的数据源和事务管理都是Spring来管理
	 -->
  <!-- default="development":默认使用哪个环境 -->
  <environments default="development">
  <!-- id="testEnv"是当前环境的唯一标识 -->
  <environment id="testEnv">
  	<transactionManager type=""/>
  	<dataSource type="">
  		<property name="driver" value="jdbc:mysql://47.110.152.31:3333/user_db"/>
        <property name="url" value="${jdbcurl}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
  	</dataSource>
  </environment>
  
  
    <environment id="development">
      <transactionManager type="JDBC"/>
      <!-- 配置连接池 -->
      <dataSource type="POOLED">
      	<!-- ${}取出配置文件中的值 -->
        <property name="driver" value="${driverclass}"/>
        <property name="url" value="${jdbcurl}"/>
        <property name="username" value="${username:root}"/><!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'root' -->
        <property name="password" value="${password:123456}"/>
      </dataSource>
    </environment>
  </environments>
  
  <!-- 7、mybatis用来考虑数据库移植性的 -->
  <databaseIdProvider type="DB_VENDOR">
  	<!-- name="":数据库厂商标识;value="":给这个标识起一个好用的名字
  		MySQL、Oracle、SQL Server
  	 -->
  	<property name="MySQL" value="mysql"/>
  	<property name="SQL Server" value="sqlserver"/>
  	<property name="Oracle" value="oracle"/>
  </databaseIdProvider>
  
  <!-- 8、写好的sql映射文件,需要使用mappers注册进来 -->
  <mappers>
  	<!-- 
  	url:可以从磁盘或者网络路径引用
  	resource:在类路径下找sql映射文件
  	class:直接引用接口的全类名
  		可以将xml放在和dao接口同目录下,而且文件名和接口名一致;
  		class的另一种用法
  	 -->
  	 
     <!-- <mapper class="com.lql.dao.EmployeeDao"/> -->
     <!-- <mapper class="com.lql.dao.EmployeeDaoAnnotation"/>
     <mapper resource="mybatis/EmployeeDao.xml"/>  -->
     <!-- 配合使用:重要的dao可以写配置;
     	简单的dao就直接标注解;
      -->
      
      <!-- 批量注册 name="":dao所在的包名 -->
      <package name="com.lql.dao"/>
  </mappers>
</configuration>

3.dbconfig.properties

username=root
password=123456
jdbcurl=jdbc:mysql://localhost:3306/test?serverTimezone=UTC
driverclass=com.mysql.cj.jdbc.Driver

4.EmployeeDao.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 namespace="com.lql.dao.EmployeeDao">
  <!-- public Employee getEmpById(Integer emp_id);参数类型不用写 -->
  <!-- 默认这个查询是不区分环境 -->
  <select id="getEmpById" resultType="com.lql.bean.Employee">
  		<!-- sql语句不要写分号 -->
  		select * from employee where emp_id=#{emp_id}
  </select>
  
  <!-- 如果能精确匹配就精确,不能就用模糊的 -->
  <select id="getEmpById" resultType="com.lql.bean.Employee" databaseId="mysql">
  		<!-- sql语句不要写分号 -->
  		select * from employee where emp_id=#{emp_id}
  </select>
  
  <select id="getEmpById" resultType="com.lql.bean.Employee" databaseId="oracle">
  		<!-- sql语句不要写分号 -->
  		select * from employee where emp_id=#{emp_id}
  </select>
  
<!-- public int updateEmployee(Employee employee);
增删改不用写返回值类型;增删改是返回影响多少行
mybatis自动判断,如果是数字(int,long)
如果是boolean(影响0行自动封装false,否则true)
#{属性名}:从传入的参数对象中取出对应属性的值
-->
<update id="updateEmployee">
	UPDATE employee 
	SET emp_name=#{emp_name},emp_gender=#{emp_gender},emp_email=#{emp_email}
	WHERE emp_id=#{emp_id}
</update>

<!-- public int deleteEmployee(Integer emp_id); -->
<delete id="deleteEmployee">
	DELETE FROM employee WHERE emp_id=#{emp_id}
</delete>

<!-- public int insertEmployee(Employee employee);-->
<insert id="insertEmployee">
	INSERT INTO employee(emp_name,emp_gender,emp_email) 
	VALUES(#{emp_name},#{emp_gender},#{emp_email})
</insert>

  </mapper>

4.MyBatisCRUDTest.java

package com.lql.test;

import java.io.IOException;
import java.io.InputStream;

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.junit.Before;
import org.junit.Test;

import com.lql.bean.Employee;
import com.lql.dao.EmployeeDao;
import com.lql.dao.EmployeeDaoAnnotation;

public class MyBatisCRUDTest {
	
	//工厂一个
	SqlSessionFactory sqlSessionFactory;
	
	@Before
	public void initSqlSessionFactory() throws IOException{
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}
	
	@Test
	public void testAnnotation(){
		SqlSession openSession = sqlSessionFactory.openSession();
		EmployeeDaoAnnotation mapper = openSession.getMapper(EmployeeDaoAnnotation.class);
		Employee empById = mapper.getEmpById(5);
		System.out.println(empById);
		
		EmployeeDao mapper2 = openSession.getMapper(EmployeeDao.class);
		System.out.println(mapper2);
		Employee empById2 = mapper2.getEmpById(1);
		System.out.println(empById2);
		
	}

5.EmployeeDaoAnnotation.java

package com.lql.dao;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import com.lql.bean.Employee;

public interface EmployeeDaoAnnotation {

	@Select("select * from employee where emp_id=#{emp_id}")
	public Employee getEmpById(Integer emp_id);
	
	@Update("UPDATE employee "
			+ "SET emp_name=#{emp_name},emp_gender=#{emp_gender},emp_email=#{emp_email}"
			+ " WHERE emp_id=#{emp_id}")
	public int updateEmployee(Employee employee);
	
	@Delete("DELETE FROM employee WHERE emp_id=#{emp_id}")
	public boolean deleteEmployee(Integer emp_id);
	
	@Insert("INSERT INTO employee(emp_name,emp_gender,emp_email) "
			+ "VALUES(#{emp_name},#{emp_gender},#{emp_email})")
	public int insertEmployee(Employee employee);
}



四、SQL映射文件

  • 映射文件指导着MyBatis如何进行数据库增删改查,有着非常重要的意义

1、增删改元素

image-20201215161245376


2、各种配置

3、MyBatis_sqlMapper

1.总体架构:

Snipaste_2020-12-21_08-45-46


2.bean

package com.lql.bean;

public class Cat {

	
	private Integer id;
	
	private String name;
	
	private Integer age;
	
	private Integer gender;

	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 Integer getGender() {
		return gender;
	}

	public void setGender(Integer gender) {
		this.gender = gender;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Cat [id=" + id + ", name=" + name + ", age=" + age + ", gender=" + gender + "]";
	}
		
}

*****************************************************
package com.lql.bean;

import org.apache.ibatis.type.Alias;

@Alias("emp")
public class Employee {
	
	private Integer emp_id;
	
	private String emp_name;
	
	private Integer emp_gender;
	
	private String emp_email;

	public Employee() {
		super();
	}

	public Employee(Integer emp_id, String emp_name, Integer emp_gender, String emp_email) {
		super();
		this.emp_id = emp_id;
		this.emp_name = emp_name;
		this.emp_gender = emp_gender;
		this.emp_email = emp_email;
	}

	public Integer getEmp_id() {
		return emp_id;
	}

	public void setEmp_id(Integer emp_id) {
		this.emp_id = emp_id;
	}

	public String getEmp_name() {
		return emp_name;
	}

	public void setEmp_name(String emp_name) {
		this.emp_name = emp_name;
	}

	public Integer getEmp_gender() {
		return emp_gender;
	}

	public void setEmp_gender(Integer emp_gender) {
		this.emp_gender = emp_gender;
	}

	public String getEmp_email() {
		return emp_email;
	}

	public void setEmp_email(String emp_email) {
		this.emp_email = emp_email;
	}

	@Override
	public String toString() {
		return "Employee [emp_id=" + emp_id + ", emp_name=" + emp_name + ", emp_gender=" + emp_gender + ", emp_email="
				+ emp_email + "]";
	}
	
}

*****************************************************
package com.lql.bean;

/**
 * 🔑表
 * @author 陆乾龙
 *
 */
public class Key {

	private Integer id;//钥匙的id
	
	private String keyName;//钥匙名
	
	private Lock lock;//当前钥匙能开哪个锁
	
	

	public Lock getLock() {
		return lock;
	}

	public void setLock(Lock lock) {
		this.lock = lock;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getKeyName() {
		return keyName;
	}

	public void setKeyName(String keyName) {
		this.keyName = keyName;
	}

	@Override
	public String toString() {
		return "Key [id=" + id + ", keyName=" + keyName + ", lock=" + lock + "]";
	}	
	
}

*****************************************************
package com.lql.bean;

import java.util.List;

/**
 * 🔒表
 * @author 陆乾龙
 *
 */
public class Lock {

	private Integer id;
	
	private String lockName;
	
	//查🔒的时候把所有的🔑也查出来
	private List<Key> keys;
	//1-1关联  1-n关联  n-n关联
	/**
	 * 一个key开一把lock:1-1
	 * 一个lock有多把key:1-n
	 * 从key表看lock:n-1
	 * 1-n,n-1,n-n:外键应该放在哪个表?
	 * 一对多:外键一定放在多的一端
	 * 多对多:中间表存储对应关系
	 */
	
	
	public List<Key> getKeys() {
		return keys;
	}
	public void setKeys(List<Key> keys) {
		this.keys = keys;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getLockName() {
		return lockName;
	}
	public void setLockName(String lockName) {
		this.lockName = lockName;
	}
	@Override
	public String toString() {
		return "Lock [id=" + id + ", lockName=" + lockName + ", keys=" + keys + "]";
	}
	
}


3.dao

package com.lql.dao;

import com.lql.bean.Cat;

public interface CatDao {

	public Cat getCatById(Integer id);
}

*****************************************************
package com.lql.dao;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;

import com.lql.bean.Employee;

public interface EmployeeDao {

	//列名作为key,值作为value
	public Map<String, Object> getEmpByIdReturnMap(Integer emp_id);
	
	public List<Employee> getAllEmps();
	
	/**
	 * key就是这个记录的主键,value就是这条记录封装好的对象;
	 * @return
	 * 
	 * 把查询的记录的id的值作为key封装这个map:
	 * @MapKey("emp_id")
	 */
	@MapKey("emp_id")
	public Map<Integer, Employee> getAllEmpsReturnMap();
	
	public Employee getEmpById(Integer emp_id);
	public Employee getEmpByIdAndEmpName(@Param("emp_id")Integer emp_id,@Param("emp_name")String emp_name);
	public Employee getEmpByIdAndEmpName1(Map<String, Object> map);
	
	public int updateEmployee(Employee employee);
	public boolean deleteEmployee(Integer emp_id);
	public int insertEmployee(Employee employee);
	
	public int insertEmployee2(Employee employee);
}

*****************************************************
package com.lql.dao;

import java.util.List;

import com.lql.bean.Key;

public interface KeyDao {

	/**
	 * 将🔑和🔒ℹ一起查出
	 * @param id
	 * @return
	 */
	public Key getKeyById(Integer id);
	
	public Key getKeyByIdSimple(Integer id);
	
	public List<Key> getKeysByLockId(Integer id);
}

*****************************************************
package com.lql.dao;

import com.lql.bean.Lock;

public interface LockDao {

	//查🔒的时候将所有🔑也查出来
	public Lock getLockById(Integer id);
	
	public Lock getLockByIdSimple(Integer id);
	
	public Lock getLockByIdByStep(Integer id);
}


4.dao.xml

&&&&&&&&&&&&&&&&&&&&&&& CatDao.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 namespace="com.lql.dao.CatDao">
	<!-- getCatById(Integer)
	resultType="com.lql.bean.Cat":使用默认规则:属性列名一一对应
	resultMap="mycat":查出数据封装结果的时候,使用mycat自定义的规则
	 -->
	<select id="getCatById" resultMap="mycat">
		<!-- select id,cname name,cage age,cgender gender from t_cat where id=#{id}  -->
		select * from t_cat where id=#{id} 
	</select>
	
	<!-- 自定义结果集:自己定义每一列数据和javaBean的映射规则
	type="":指定为哪个javaBean自定义封装规则;全类名
	id="":唯一标识,让别名在后面引用
	 -->
	<resultMap type="com.lql.bean.Cat" id="mycat">
		<!-- 指定主键列的对应规则:
		column="id":指定那一列是主键列
		property="":指定cat的哪个属性封装id这一列数据
		 -->
		<id property="id" column="id"/>
		<!-- 普通列 -->
		<result property="name" column="cname"/>
		<result property="age" column="cage"/>
		<result property="gender" column="cgender"/>
	</resultMap>
</mapper>


&&&&&&&&&&&&&&&&&&&&&&& EmployeeDao.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 namespace="com.lql.dao.EmployeeDao">
<!-- namespace="com.lql.dao.EmployeeDao":写dao接口的全类名 -->
<!-- 这个文件中能写的所有标签:
cache:和缓存有关
cache-ref:和缓存有关
parameterMap:参数map;废弃的。。。原本是来做复杂参数映射的;
resultMap:结果集映射;自定义结果集的封装规则
sql:抽取可重用的sql;

delete、update、insert、select:增删改查;
databaseId:指定这个CRUD属于哪个数据库的
 -->
 
 	<!-- public int insertEmployee(Employee employee);-->
 	<!-- 
 	让MyBatis自动的将自增id赋值给传入的employee对象的id属性
 		useGeneratedKeys="true":原生jdbc获取自增主键的方法;
 		keyProperty="":将刚才自增的id封装给哪个属性
 	 -->
	<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="emp_id">
		<!-- 查询主键
		order="BEFORE":
			在核心sql语句之前先运行一个查询sql查到id;将查到的id赋值给javaBean的哪个属性
		 -->
		<selectKey order="BEFORE" keyProperty="emp_id">
			select max(emp_id) from employee
		</selectKey>
		INSERT INTO employee(emp_name,emp_gender,emp_email) 
		VALUES(#{emp_name},#{emp_gender},#{emp_email})
	</insert>
	
	<!-- public int insertEmployee2(Employee employee); -->
	<insert id="insertEmployee2">
		<!-- 查询主键
		order="BEFORE":
			在核心sql语句之前先运行一个查询sql查到id;将查到的id赋值给javaBean的哪个属性
		 -->
		<selectKey order="BEFORE" resultType="integer" keyProperty="emp_id">
			select max(emp_id)+1 from employee
		</selectKey>
		INSERT INTO employee(emp_id,emp_name,emp_gender,emp_email) 
		VALUES(#{emp_id},#{emp_name},#{emp_gender},#{emp_email})
	</insert>

    <update id="updateEmployee" >
	UPDATE employee 
	SET emp_name=#{emp_name},emp_gender=#{emp_gender},emp_email=#{emp_email}
	WHERE emp_id=#{emp_id}
    </update>

	<!-- public int deleteEmployee(Integer emp_id); -->
	<delete id="deleteEmployee" >
		DELETE FROM employee WHERE emp_id=#{emp_id}
	</delete>
	
	<!-- 传参到底能传哪些 -->
	<!-- public Employee getEmpById(Integer emp_id); -->
    <select id="getEmpById" resultType="com.lql.bean.Employee" >
  		select * from employee where emp_id=#{hahaha}
    </select>
    
    <!-- Caused by: org.apache.ibatis.binding.BindingException:
     Parameter 'emp_id' not found. 
     Available parameters are [arg1, arg0, param1, param2]
     -->
    
    <!-- public Employee getEmpByIdAndEmpName(Integer emp_id,String emp_name); -->
    <select id="getEmpByIdAndEmpName" resultType="com.lql.bean.Employee">
    	select * from employee where emp_id=#{emp_id} and emp_name=#{emp_name}
    </select>
    
    <!-- public Employee getEmpByIdAndEmpName1(Map<String, Object> map); -->
    <select id="getEmpByIdAndEmpName1" resultType="com.lql.bean.Employee">
    	select * from ${tableName} where emp_id=#{emp_id,jdbcType=INTEGER} and emp_name=#{emp_name}
    </select>
    
    <!-- 现象:
    	1)、单个参数
    		基本类型:
    			取值:#{随便写}
    		传入Pojo
    	2)、多个参数
    		public Employee getEmpByIdAndEmpName(Integer emp_id,String emp_name)
    		取值:#{参数名}是无效了;
    		可用:arg0,arg1或者param1,param2
    		原因:只要是传入了多个参数;mybatis会自动的将这些参数封装在一个map中,
    			封装时使用的key就是参数的索引和参数的第几个表示
    			Map<String,Object> map = new HashMap<>();
    			map.put("1",传入的值);map.put("2",传入的值);
    		#{key}就是从这几个map中取值;
    	3)、@Param("")为参数指定key,命名参数,我们以后也推荐这么做;
    		我们可以告诉mybatis,封装参数map的时候别乱来,使用我们指定的key
    	
    	4)、传入了pojo(javaBean)
    		取值:#{pojo的属性名}
    	
    	5)、传入了map;将多个要使用的参数封装起来
    		取值:#{key}
    	
    	扩展:多个参数:自动封装map;
    	method01(@Param("emp_id")Integer emp_id,String emp_name,Employee employee);
    	Integer id -> #{emp_id}
    	String emp_name -> #{param2}
    	Employee employee(取出这里面的email) -> #{param3.email}
    	
    	无论传入什么参数都要能正确的取出值;
    	#{key/属性名}
    	1)、#{key,jdbcType=INTEGER};
    	javaType、jdbcType、mode、numericScale、resultMap、typeHandler、jdbcTypeName、expression
    	只有jdbcType才可能是需要被指定的;
    		默认不指定jdbcType:mysql没问题,oracle没问题;
    		万一传入的数据是null:mysql插入null没问题,【oracle不知道null到底是什么类型】
    	
    	实际上在mybatis中,有两种取值方式:
    	#{属性名}:是预编译的方式,参数的位置都是?替代,参数后来都是预编译设置进去的;安全,不会有sql注入问题
    	${属性名}:不是参数预编译,而是直接和sql语句进行拼串;不安全:
    		//emp_id=1 or 1=1 or and emp_name=
    		传入一个'1 or 1=1 or'
    		有使用场景:sql语句只有参数位置是支持预编译的:
    		log_2020_12、log_2021_1(动态表名)
    		select * from log_2021_1 where emp_id=1 and emp_name=?
    	
    	
    	emp_id=${emp_id} and emp_name=#{emp_name}
    	select * from employee where emp_id=1 and emp_name=?
    	
    	emp_id=#{emp_id} and emp_name=#{emp_name}
    	select * from employee where emp_id=? and emp_name=?
    	
    	一般都是使用#{};安全;在不支持预编译参数的位置要进行取值就使用${};
     -->
     
     <!-- public List<Employee> getAllEmps(); -->
     <!-- resultType="":如果返回的是集合,写的是集合里面元素的类型 -->
     <select id="getAllEmps" resultType="com.lql.bean.Employee">
     	select * from employee
     </select>
     
     <!-- 查询返回一个map -->
     <!-- public Map<String, Object> getEmpByIdReturnMap(Integer emp_id); -->
     <select id="getEmpByIdReturnMap" resultType="map">
     	select * from employee where emp_id=#{emp_id}
     </select>
     
     <!-- 查询多个返回一个map;查询多个情况下,集合里面写元素类型 -->
     <!-- public Map<Integer, Employee> getAllEmpsReturnMap(); -->
     <select id="getAllEmpsReturnMap" resultType="com.lql.bean.Employee">
     	select * from employee
     </select>
</mapper>


&&&&&&&&&&&&&&&&&&&&&&& KeyDao.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 namespace="com.lql.dao.KeyDao">

	<!-- public List<Key> getKeysByLockId(Integer id); 
	按照锁id查出所有的key
	-->
	<select id="getKeysByLockId" resultType="com.lql.bean.Key">
		SELECT * FROM t_key WHERE lockid = #{id};
	</select>

	<!-- public Key getKeyByIdSimple(Integer id); -->
	<!-- 查询key的时候也可以带上锁信息 -->
	<!-- id	keyname	lockid -->
	<!-- 
	private Integer id;//钥匙的id
	private String keyName;//钥匙名
	private Lock lock;//当前钥匙能开哪个锁
	 -->
	<select id="getKeyByIdSimple" resultMap="mykey02">
		select * from t_key where id = #{id}
	</select>
	<resultMap type="com.lql.bean.Key" id="mykey02">
		<id property="id" column="id"/>
		<result property="keyName" column="keyname"/>
		<!-- 告诉mybatis自己去调用一个查询查锁
		select="":指定一个查询sql的唯一标识;
		mybatis自动调用指定的sql将查出的lock封装进来
		public Lock getLockByIdSimple(Integer id);需要传入锁id
		告诉mybatis把那一列的值传过去:
		column="":指定将哪一列的数据传递过去
		 -->
		<association property="lock" select="com.lql.dao.LockDao.getLockByIdSimple"
		column="lockid" fetchType="lazy"></association>
	</resultMap>


	<!-- getKeyById(Integer) -->
	<select id="getKeyById" resultMap="mykey">
		SELECT k.id,k.keyname,k.lockid,l.id lid,l.lockname 
		FROM t_key k LEFT JOIN t_lock l ON k.lockid = l.id 
		WHERE k.id = #{id};
	</select>
	
	<!-- 自定义封装规则:使用级联属性封装 联合查询出来的结果-->
	<!-- <resultMap type="com.lql.bean.Key" id="mykey">
		<id property="id" column="id"/>
		<result property="keyName" column="keyname"/>
		<result property="lock.id" column="lid"/>
		<result property="lock.lockName" column="lockname"/>
	</resultMap> -->
	
	<!-- mybatis推荐的 -->
	<resultMap type="com.lql.bean.Key" id="mykey">
		<id property="id" column="id"/>
		<result property="keyName" column="keyname"/>
		<!-- 接下来的属性是一个对象,自定义这个对象的封装规则;
		使用association:表示联合了一个对象
		javaType="":指定这个属性的类型
		-->
		<association property="lock" javaType="com.lql.bean.Lock">
			<!-- 定义lock属性对应的这个Lock对象如何封装 -->
			<id property="id" column="lid"/>
			<result property="lockName" column="lockname"/>
		</association>
	</resultMap>
</mapper>


&&&&&&&&&&&&&&&&&&&&&&& LockDao.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 namespace="com.lql.dao.LockDao">

	<!-- public Lock getLockByIdByStep(Integer id); -->
	<select id="getLockByIdByStep" resultMap="mylockstep">
		select * from t_lock where id=#{id}
	</select>
	<!-- collection分步查询 -->
	<resultMap type="com.lql.bean.Lock" id="mylockstep">
		<id property="id" column="id"/>
		<result property="lockName" column="lockname"/>
		<!-- collection指定集合类型的属性封装规则 -->
		<collection property="keys" 
		select="com.lql.dao.KeyDao.getKeysByLockId"
		column="{id=id}"></collection>
		<!-- {key1=列名,key2=列名} -->
	</resultMap>

	<!-- public Lock getLockByIdSimple(Integer id); -->
	<select id="getLockByIdSimple" resultType="com.lql.bean.Lock">
		select * from t_lock where id=#{id}
	</select>


	<!-- public Lock getLockById(Integer id); -->
	<select id="getLockById" resultMap="mylock">
		SELECT l.*,k.id kid,k.keyname,k.lockid 
		FROM t_lock l LEFT JOIN t_key k 
		ON l.id=k.lockid WHERE l.id = #{id}
	</select>
	<!-- 
		private Integer id;
		private String lockName;
		//查锁的时候把所有的钥匙也查出来
		private List<Key> keys;
		
		id	lockname	kid	keyname	lockid
		3	303办公室的锁	3	303钥匙1	3
		3	303办公室的锁	4	303钥匙2	3
		3	303办公室的锁	5	303钥匙3	3
	 -->
	<resultMap type="com.lql.bean.Lock" id="mylock">
		<id property="id" column="id"/>
		<result property="lockName" column="lockname"/>
		<!-- 
		collection:定义集合元素的封装
			property="":指定哪个属性是集合属性
			javaType:指定对象类型(association)
			ofType:指定集合里面元素的类型
		 -->
		<collection property="keys" ofType="com.lql.bean.Key">
			<!-- 标签体中集合中这个元素的封装规则 -->
			<id property="id" column="kid"/>
			<result property="keyName" column="keyname"/>
		</collection>
	</resultMap>
</mapper>

5.mybatis-config.xml

<?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>

	<settings>
		<!-- 开启延迟加载开关 -->
		<setting name="lazyLoadingEnabled" value="true"/>
		<!-- 开启属性按需加载(默认开启) -->
		<!-- <setting name="aggressiveLazyLoading" value="false"/> -->
	</settings>
  <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/test?serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
      <package name="com.lql.dao"/>
  </mappers>
</configuration>

6、MyBatisTest.java

package com.lql.test;

import static org.junit.Assert.*;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

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.junit.Before;
import org.junit.Test;

import com.lql.bean.Cat;
import com.lql.bean.Employee;
import com.lql.bean.Key;
import com.lql.bean.Lock;
import com.lql.dao.CatDao;
import com.lql.dao.EmployeeDao;
import com.lql.dao.KeyDao;
import com.lql.dao.LockDao;

public class MyBatisTest {

	//工厂一个
	SqlSessionFactory sqlSessionFactory;
		
	@Before
	public void initSqlSessionFactory() throws IOException{
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}
	
	@Test
	public void test() {
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			EmployeeDao employeeDao = openSession.getMapper(EmployeeDao.class);
			Employee empById = employeeDao.getEmpById(1);
			System.out.println(empById);
			
			
		} finally {
			openSession.close();
		}
	}
	
	@Test
	public void test02() {
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			EmployeeDao employeeDao = openSession.getMapper(EmployeeDao.class);
			/*Employee employee = new Employee(null, "haha", 0, "aaa");
			int i = employeeDao.insertEmployee(employee);
			System.out.println("--->"+i);
			System.out.println("刚才插入的id:"+employee.getEmp_id());*/
			Employee employee = new Employee(null, "haha", 0, "aaa");
			employeeDao.insertEmployee2(employee);
			
			System.out.println(employee.getEmp_id());
			openSession.commit();
		} finally {
			openSession.close();
		}
	}
	
	@Test
	public void test03() {
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			EmployeeDao employeeDao = openSession.getMapper(EmployeeDao.class);
			/*Employee empByIdAndEmpName = employeeDao.getEmpByIdAndEmpName(1, "admin");
			System.out.println(empByIdAndEmpName);
			
			Employee empById = employeeDao.getEmpById(1);
			System.out.println(empById);*/
			
			Map<String, Object> map = new HashMap<String,Object>();
			map.put("emp_id", 1);
			map.put("emp_name", "admin");
			map.put("tableName", "employee");
			Employee empByIdAndEmpName2 = employeeDao.getEmpByIdAndEmpName1(map);
			System.out.println(empByIdAndEmpName2);
		} finally {
			openSession.close();
		}
	}
	
	@Test
	public void test04() {
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			EmployeeDao employeeDao = openSession.getMapper(EmployeeDao.class);
			//查询多条记录封装list
			/*List<Employee> allEmps = employeeDao.getAllEmps();
			for (Employee employee : allEmps) {
				System.out.println(employee);
			}*/
			
			//查询单条记录封装map
			/*Map<String, Object> map = employeeDao.getEmpByIdReturnMap(1);
			System.out.println(map);*/
			
			//查询多条记录封装map
			Map<Integer, Employee> map = employeeDao.getAllEmpsReturnMap();
			System.out.println(map);
			Employee employee = map.get(1);
			System.out.println(employee.getEmp_name());
			
		} finally {
			openSession.close();
		}
	}
	
	/**
	 * 默认mybatis自动封装结果集;
	 * 1)、按照列名和属性名一一对应的规则(不区分大小写);
	 * 2)、如果不一一对应:
	 * 		1)、开启驼峰命名法(满足驼峰命名规则 aaa_bbb aaaBbb)
	 * 		2)、起别名
	 */
	@Test
	public void test05() {
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			CatDao mapper = openSession.getMapper(CatDao.class);
			Cat catById = mapper.getCatById(2);
			System.out.println(catById);
			
		} finally {
			openSession.close();
		}
	}
	
	/**
	 * 联合查询情况下
	 * 1、使用级联属性封装联合查询后的所有结果
	 */
	@Test
	public void test06() {
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			KeyDao mapper = openSession.getMapper(KeyDao.class);
			Key keyById = mapper.getKeyById(2);
			System.out.println(keyById);
			
		} finally {
			openSession.close();
		}
	}
	
	@Test
	public void test07() {
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			LockDao mapper = openSession.getMapper(LockDao.class);
			Lock lockById = mapper.getLockById(3);
			System.out.println(lockById);
			System.out.println("所有🔑如下:");
			List<Key> keys = lockById.getKeys();
			for (Key key : keys) {
				System.out.println(key);
			}
		} finally {
			openSession.close();
		}
	}
	
	
	@Test
	public void test08() {
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			LockDao mapper = openSession.getMapper(LockDao.class);
			Lock lockById = mapper.getLockById(3);
			System.out.println(lockById);
			System.out.println("所有🔑如下:");
			List<Key> keys = lockById.getKeys();
			for (Key key : keys) {
				System.out.println(key);
			}
		} finally {
			openSession.close();
		}
	}
	
	/**
	 * 分步查询
	 * 查询🔑的时候顺便查出🔒
	 * 1)、Key key = keyDao.getKeyById(1);
	 * 2)、Lock lock = lockDao.getLockById(1);
	 * @throws InterruptedException 
	 */
	@Test
	public void test09() throws InterruptedException {
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			KeyDao mapper = openSession.getMapper(KeyDao.class);
			Key keyByIdSimple = mapper.getKeyByIdSimple(1);
			//严重性能;
			System.out.println(keyByIdSimple.getKeyName());
			//按需加载(需要的时候再去查询)全局开启按需加载策略
			//延迟加载,不着急加载(查询对象)
			Thread.sleep(3000);
			String lockName = keyByIdSimple.getLock().getLockName();
			System.out.println(lockName);
		} finally {
			openSession.close();
		}
	}
	
	/**
	 * 一般我们在工作的时候:写成两个方法
	 * public Key getKeySimple(Integer id);
	 * 
	 * 推荐都来写连接查询
	 * public Key getKeyAssicate()
	 * @throws InterruptedException
	 */
	@Test
	public void test10() throws InterruptedException {
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			LockDao mapper = openSession.getMapper(LockDao.class);
			Lock lockByIdByStep = mapper.getLockByIdByStep(3);
			System.out.println(lockByIdByStep.getLockName());
			
			List<Key> keys = lockByIdByStep.getKeys();
			for (Key key : keys) {
				System.out.println(key.getKeyName());
			}
		} finally {
			openSession.close();
		}
	}
	
	
}



五、动态sql

1、总体架构:

image-20201221091302369

2、bean

package com.lql.bean;

import java.util.Date;

public class Teacher {

	private Integer id;
	
	private String name;
	
	private String course;
	
	private String address;
	
	private Date birth;

	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 getCourse() {
		return course;
	}

	public void setCourse(String course) {
		this.course = course;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public Date getBirth() {
		return birth;
	}

	public void setBirth(Date birth) {
		this.birth = birth;
	}

	@Override
	public String toString() {
		return "Teacher [id=" + id + ", name=" + name + ", course=" + course + ", address=" + address + ", birth="
				+ birth + "]";
	}
	
	
}


3、dao

package com.lql.dao;

import java.util.List;

import org.apache.ibatis.annotations.Param;

import com.lql.bean.Teacher;

public interface TeacherDao {

	public Teacher getTeacherById(Integer id);
	
	public List<Teacher> getTeacherByCondition(Teacher teacher);
	
	public List<Teacher> getTeacherByIdIn(@Param("ids")List<Integer> ids);
	
	public List<Teacher> getTeacherByConditionChoose(Teacher teacher);
	
	public int updateTeacher(Teacher teacher);
}


4、dao.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 namespace="com.lql.dao.TeacherDao">
<resultMap type="com.lql.bean.Teacher" id="teacherMap">
	<id property="id" column="id"/>
	<result property="address" column="address"/>
	<result property="birth" column="birth_date"/>
	<result property="course" column="class_name"/>
	<result property="name" column="t_name"/>
</resultMap>

<!-- 抽取可重用的sql语句 -->
<sql id="selectSql">select * from t_teacher</sql>
<!-- id	t_name	class_name	address	birth_date -->

	<!-- public Teacher getTeacherById(Integer id); -->
	<select id="getTeacherById" resultMap="teacherMap">
		<include refid="selectSql"></include>
		<!-- select * from t_teacher --> where id=#{id}
	</select>
	
	<!-- public List<Teacher> getTeacherByCondition(Teacher teacher); -->
	<select id="getTeacherByCondition" resultMap="teacherMap">
		select * from t_teacher
		<!-- test="":编写判断条件
		id!=null:取出传入的javaBean属性中的id值,判断其是否为空
		 -->
		 <!-- <where>标签可以帮我们去除掉前面的and -->
		 <!-- <trim>标签截取字符串
		 prefix="":前缀;为我们下面的sql整体添加一个前缀;
		 prefixOverrides="":去除整体字符串前面多余的字符;
		 suffix="":为整体添加一个后缀
		 suffixOverrides="":后面哪个多了,可以去掉
		  -->
		  <!-- 我们的查询条件就放在where标签中,每个and写在前面,where帮我们自动取出前面多余的and -->
		 <trim prefix="where" 
		  suffixOverrides="and ">
		<if test="id!=null">
			id > #{id} and
		</if> 
		<!-- 空串""
		 and:&&
		 or:||;
		 if():传入非常强大的判断条件;
		 
		 OGNL表达式:对象导航图(相当于级联属性)
		 方法、静态方法、构造器、xxx
		 在mybatis中,传入的参数可以用来做判断;
		 额外还有两个东西:
		 	_parameter:代表传入来的参数:
		 		1)、传入了单个参数:_parameter就代表这个参数
		 		2)、传入了多个参数:_parameter就代表多个参数集合起来的map
		 	_databaseId:代表当前环境
		 		如果配置了databaseIdProvider,_parameter就有值
		  -->
		  
		  <!-- 绑定一个表达式的值到一个变量(不推荐) -->
		<bind name="_name" value="'%'+name+'%'"/>  
		<if test="name!=null &amp;&amp; !name.equals(&quot;&quot;)">
			 t_name like #{_name} and
		</if>
		<if test="birth!=null">
			 birth_date &lt; #{birth} and
		</if>
		</trim>
		
	</select>
	
	<!-- public List<Teacher> getTeacherByIdIn(List<Integer> ids); -->
	<select id="getTeacherByIdIn" resultMap="teacherMap">
		SELECT * FROM t_teacher WHERE id IN 
		<!-- 帮我们遍历集合的;
		collection="":指定要遍历的集合的key
		close="" :以什么结束
		index="i" :索引
			如果遍历的是一个list:
				index:指定的变量保存了当前索引;
				item:就是保存当前遍历的元素的值;
			如果遍历的是一个map:
				index:指定的变量就是保存了当前遍历的元素的key
				item:就是保存当前遍历的元素的值;
		item="变量名" :每次遍历出的元素起一个变量名方便引用
		open="" :以什么开始
		separator="":每次遍历元素的分隔符
		 -->
		 <if test="ids.size >0">
		<foreach collection="ids" item="id_item" 
		open="(" close=")" separator=",">
			#{id_item}
		</foreach>
		</if>
	</select>
	
	<!-- public List<Teacher> getTeacherByConditionChoose(Teacher teacher); -->
	<select id="getTeacherByConditionChoose" resultMap="teacherMap">
		SELECT * FROM t_teacher
		<where>
			<choose>
				<when test="id!=null">
					id=#{id}
				</when>
				<when test="name!=null and !name.equals(&quot;&quot;)">
					t_name=#{name}
				</when>
				<when test="birth!=null">
					birth_date=#{birth}
				</when>
				<otherwise>
					1=1
				</otherwise>
			</choose>
		</where>
	</select>
	
	<!-- public int updateTeacher(Teacher teacher); -->
	<update id="updateTeacher">
		UPDATE t_teacher
		<set>
		<if test="name!=null and !name.equals(&quot;&quot;)">
			t_name=#{name},
		</if>
		<if test="course!=null and !course.equals(&quot;&quot;)">
			class_name=#{course},
		</if>
		<if test="address!=null and !address.equals(&quot;&quot;)">
			address=#{address},
		</if>
		<if test="birth!=null">
			birth_date=#{birth}
		</if>
		</set>
		<where>
			id=#{id}
		</where>
	</update>
</mapper>

5、mybatis-config.xml

<?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>

	<settings>
		<!-- 开启延迟加载开关 -->
		<setting name="lazyLoadingEnabled" value="true"/>
		<!-- 开启属性按需加载(默认开启) -->
		<!-- <setting name="aggressiveLazyLoading" value="false"/> -->
	</settings>
  <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/test?serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
      <package name="com.lql.dao"/>
  </mappers>
</configuration>

6、MyBatisTest.java

package com.lql.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

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.junit.Before;
import org.junit.Test;

import com.lql.bean.Teacher;
import com.lql.dao.TeacherDao;

public class MyBatisTest {

	//工厂一个
	SqlSessionFactory sqlSessionFactory;
		
	@Before
	public void initSqlSessionFactory() throws IOException{
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}
	
	@Test
	public void test() {
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			TeacherDao mapper = openSession.getMapper(TeacherDao.class);
			/*Teacher teacher = new Teacher();
			teacher.setId(1);
			teacher.setName("%a%");
			//teacher.setBirth(new Date());
			List<Teacher> list = mapper.getTeacherByCondition(teacher);*/
			
			//List<Teacher> list = mapper.getTeacherByIdIn(Arrays.asList(1,2,3,4,5));
			Teacher teacher = new Teacher();
			//teacher.setId(1);
			//teacher.setName("%a%");
			List<Teacher> list = mapper.getTeacherByConditionChoose(teacher);
			
			System.out.println(list);
		} finally {
			openSession.close();
		}
	}
	
	@Test
	public void test02() {
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			TeacherDao mapper = openSession.getMapper(TeacherDao.class);
			Teacher teacher = new Teacher();
			teacher.setId(1);
			teacher.setName("hahaha");
			int i = mapper.updateTeacher(teacher);
			System.out.println(i);
			openSession.commit();
		} finally {
			openSession.close();
		}
	}
	
	public static void main(String[] args) {
		List<String> list = new ArrayList<>();
		list.add("java");
		list.add("c++");
		list.add("python");
		Iterator<String> iterator = list.iterator();
		while(iterator.hasNext()){
			System.out.println(iterator.next());
		}
	}
}



六、缓存机制

1、缓存作用

  • 暂时的存储一些数据
  • 加快系统的查询速度

2、MyBatis缓存机制

  • Map保存查询出的一些数据

  • 一级缓存

    • 线程级别的缓存
    • 本地缓存
    • SqlSession级别的缓存
    • 默认存在
  • 二级缓存

    • 全局范围的缓存
    • namespace级别的缓存
    • 除过当前的线程/SqlSession能用外其他也可以使用

3、一级缓存

  • 只要之前查询过的数据,mybatis就会保存在一个缓存中(Map)
  • 下次获取直接从缓存中拿

一级缓存失效的几种情况

  1. 不同的SqlSession对应不同的一级缓存
  2. 同一个SqlSession但是查询条件不同
  3. 同一个SqlSession两次查询期间执行了任何一次增删改操作
  4. 同一个SqlSession两次查询期间手动清空了缓存

源码位置

  • key很长(敏感)

image-20201218133253151


4、二级缓存

  • Sqlsession关闭或者提交以后,一级缓存的数据会放在二级缓存中
  • mybatis默认没有使用的

1.二级缓存的使用

1、全局配置开启二级缓存

<!-- 开启全局缓存开关(默认开启) -->
<setting name="cacheEnabled" value="true"/>

2、配置某个dao.xml文件,让其使用二级缓存

<!-- 使用二级缓存 -->
<cache/>

2.缓存原理

image-20201218145239818


5、缓存有关设置

1.全局setting的cacheEnable

  • –配置二级缓存的开关(一级缓存一直是打开的)

2.select标签的useCache属性

  • –配置这个select是否使用二级缓存(一级缓存一直是使用的)

3.sql标签的flushCache属性

  • –sql执行以后,会同时清空一级和二级缓存
  • 增删改默认flushCache=true
  • 查询默认flushCache=false

4.sqlSession.clearCache()

  • –只是用来清除一级缓存
  • 当在某一个作用域 (一级缓存Session/二级缓存Namespaces) 进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear

6、整合第三方缓存

  • MyBatis—–Cache接口
  • 整合ehcache(非常专业的java进程内的缓存框架)

1.导包

ehcache-core-2.6.11.jar(ehcache核心包) mybatis-ehcache-1.2.1.jar(ehcache的整合包)

slf4j-api-1.7.30.jar slf4j-log4j12-1.7.30.jar


2.ehcache配置文件

  • 文件名叫ehcache.xml
  • 放在类路径的根目录下(conf下)

3.在mapper.xml中配置使用自定义的缓存

<!-- 使用mybatis默认二级缓存<cache /> -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache" />

4.别的dao还要用这个缓存

<!-- 和别的dao共用一块缓存 -->
<cache-ref namespace="com.lql.dao.TeacherDao"/>

5.echcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
 <!-- 磁盘保存路径 -->
 <diskStore path="D:\44\ehcache" />
 
 <defaultCache 
   maxElementsInMemory="1" 
   maxElementsOnDisk="10000000"
   eternal="false" 
   overflowToDisk="true" 
   timeToIdleSeconds="120"
   timeToLiveSeconds="120" 
   diskExpiryThreadIntervalSeconds="120"
   memoryStoreEvictionPolicy="LRU">
 </defaultCache>
</ehcache>
 
<!-- 
属性说明:
l diskStore:指定数据在磁盘中的存储位置。
l defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
 
以下属性是必须的:
l maxElementsInMemory - 在内存中缓存的element的最大数目 
l maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
l eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
 
以下属性是可选的:
l timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
l timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
 diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
l diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
 -->

6.MyBatisTest.java

package com.lql.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

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.junit.Before;
import org.junit.Test;

import com.lql.bean.Teacher;
import com.lql.dao.TeacherDao;

public class MyBatisTest {

	//工厂一个
	SqlSessionFactory sqlSessionFactory;
		
	@Before
	public void initSqlSessionFactory() throws IOException{
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}
	
	/**
	 * 体会一级缓存
	 */
	@Test
	public void test() {
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			TeacherDao mapper = openSession.getMapper(TeacherDao.class);
			Teacher teacherById = mapper.getTeacherById(1);
			System.out.println(teacherById);
			System.out.println("=========================");
			Teacher teacherById2 = mapper.getTeacherById(1);
			System.out.println(teacherById2);
			System.out.println(teacherById == teacherById2);
		} finally {
			openSession.close();
		}
	}
	
	/**
	 * 一级缓存失效的几种情况
	 * 一级缓存是SqlSession级别的缓存;
	 * 1、不同的sqlSession使用不同的一级缓存;
	 * 		只有在同一个sqlSession期间查询到的数据会保存在这个sqlSession的缓存中;
	 * 		下次使用个这个sqlSession查询会从缓存中拿。
	 * 2、同一个方法,不同的参数,由于可能之前没查过,所以还会发新的sql;
	 * 3、在这个sqlSession期间执行任何一次增删改操作,会把缓存清空;
	 * 4、手动清空了缓存;
	 * 
	 * 每次查询,先看一级缓存中有没有,如果没有就去发送新的sql;每个sqlSession拥有自己的一级缓存;
	 * 
	 */
	@Test
	public void test02() {
		SqlSession openSession = sqlSessionFactory.openSession();
		//1、第一个会话
		TeacherDao mapper = openSession.getMapper(TeacherDao.class);
		Teacher teacher1 = mapper.getTeacherById(1);
		System.out.println(teacher1);
		System.out.println("============================");
		//执行任何一个增删改操作
		/*Teacher teacher = new Teacher();
		teacher.setId(3);
		teacher.setName("3333");
		mapper.updateTeacher(teacher);*/
		
		System.out.println("手动清空缓存");
		//清空当前sqlSession的以及缓存
		//openSession.clearCache();
		
		System.out.println("============================");
		Teacher teacher2 = mapper.getTeacherById(1);
		System.out.println(teacher2);
		
		//2、第二个会话
		/*SqlSession openSession2 = sqlSessionFactory.openSession();
		TeacherDao mapper2 = openSession2.getMapper(TeacherDao.class);
		Teacher teacher2 = mapper2.getTeacherById(1);
		System.out.println(teacher2);*/
		
		openSession.commit();
		openSession.close();
		//openSession2.close();
	}
	
	/**
	 * 体验二级缓存
	 */
	@Test
	public void test03() {
		SqlSession openSession = sqlSessionFactory.openSession();
		SqlSession openSession2 = sqlSessionFactory.openSession();
		TeacherDao mapper1 = openSession.getMapper(TeacherDao.class);
		TeacherDao mapper2 = openSession2.getMapper(TeacherDao.class);
		
		//1、第一个dao查询1号teacher
		Teacher teacher1 = mapper1.getTeacherById(1);
		System.out.println(teacher1);
		openSession.close();
		
		//1、第二个dao查询1号teacher
		Teacher teacher2 = mapper2.getTeacherById(1);
		System.out.println(teacher2);
		openSession.close();
	}
	
	/**
	 * 
	 * 1、不会出现一级缓存和二级缓存中有同一个数据:
	 * 		二级缓存中,一级缓存关闭了就有了;
	 * 		一级缓存中,二级缓存中没有此数据,就会看一级缓存,一级缓存没有就去查询数据库
	 * 2、任何时候都是先看二级缓存,再看一级缓存,如果大家都没有就去查询数据库;
	 * 二-----》一====》库
	 */
	@Test
	public void test04() {
		SqlSession openSession = sqlSessionFactory.openSession();
		TeacherDao teacherDao = openSession.getMapper(TeacherDao.class);
		Teacher teacher = teacherDao.getTeacherById(1);
		System.out.println(teacher);
		openSession.close();
		System.out.println("===============");
		
		SqlSession openSession2 = sqlSessionFactory.openSession();
		TeacherDao teacherDao2 = openSession2.getMapper(TeacherDao.class);
		Teacher teacherById = teacherDao2.getTeacherById(1);
		System.out.println(teacherById);
		
		Teacher teacherById2 = teacherDao2.getTeacherById(1);
		System.out.println(teacherById2);
		
		
		/*Teacher teacherById2 = teacherDao2.getTeacherById(1);
		System.out.println(teacherById2);
		
		System.out.println("以下是查询二号teacher:");
		Teacher teacherById3 = teacherDao2.getTeacherById(2);
		System.out.println(teacherById3);
		Teacher teacherById4 = teacherDao2.getTeacherById(2);
		System.out.println(teacherById4);
		*/
		openSession2.close();
	}
	
	
}


七、SSM整合

  • SSM
    • Spring+SpringMVC+MyBatis

1、导包

1.Spring

aop核心
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aspects-5.2.9.RELEASE.jar
    
ioc核心
commons-logging-1.1.3.jar
spring-aop-5.2.9.RELEASE.jar
spring-beans-5.2.9.RELEASE.jar
spring-context-5.2.9.RELEASE.jar
spring-core-5.2.9.RELEASE.jar
spring-expression-5.2.9.RELEASE.jar
    
jdbc核心
spring-jdbc-5.2.9.RELEASE.jar
spring-orm-5.2.9.RELEASE.jar
spring-tx-5.2.9.RELEASE.jar

测试
spring-test-5.2.9.RELEASE.jar
   

2.SpringMVC

Ajax
jackson-annotations-2.12.0.jar
jackson-core-2.12.0.jar
jackson-databind-2.12.0.jar

数据校验
hibernate-validator-6.1.7.Final.jar
hibernate-validator-annotation-processor-6.1.7.Final.jar
classmate-1.5.1.jar
jboss-logging-3.4.1.Final.jar
validation-api-2.0.1.Final.jar

上传下载
commons-fileupload-1.4.jar
commons-io-2.8.0.jar
    
jstl-jsp标准标签库
jstl.jar
standard.jar
    
验证码   
kaptcha-2.3.2.jar  
    
SpringMVC核心
spring-web-5.2.9.RELEASE.jar
spring-webmvc-5.2.9.RELEASE.jar

3.MyBatis

mybatis核心
mybatis-3.5.6.jar

和spring的整合包
mybatis-spring-2.0.6.jar
    
ehcache整合
ehcache-core-2.6.11.jar
mybatis-ehcache-1.2.1.jar
log4j-1.2.17.jar
slf4j-api-1.7.30.jar
slf4j-log4j12-1.7.30.jar

4.其他的

//数据源,驱动
mysql-connector-java-8.0.22.jar
c3p0-0.9.5.5.jar
mchange-commons-java-0.2.20.jar
//分页插件
pagehelper-5.2.0.jar
jsqlparser-3.2.jar

5.总览:

image-20201221101143992


2、写配置

0.web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>7.SSM</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
  <!-- 配置Spring容器启动 -->
  <!-- needed for ContextLoaderListener -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<!-- 指定Spring配置文件位置 -->
		<param-value>classpath:spring/applicationContext.xml</param-value>
	</context-param>

	<!-- Bootstraps the root web application context before servlet initialization -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<!-- 配置springmvc的前端控制器 -->
	<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
	<servlet>
		<servlet-name>springDispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring/applicationContext-mvc.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<!-- 两个标准配置 -->
	<!-- 字符编码 -->
	<!-- <filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>utf-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceRequestEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
		<init-param>
			<param-name>forceResponseEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping> -->
	
	<!-- 支持Rest风格的过滤器 -->
	<filter>
		<filter-name>HiddenHttpMethodFilter</filter-name>
		<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>HiddenHttpMethodFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
</web-app>

1.spring的配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
	xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

	<!-- Spring除过控制器不要,剩下的业务逻辑组件都要(包括dao,service。。) -->
	<context:component-scan base-package="com.lql">
		<!-- 扫描排除不写use-default-filters="false" -->
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>
	
	<!-- 0、导入外部配置文件 -->
	<context:property-placeholder location="classpath:dbconfig.properties"/>
	<!-- 1、配数据源 -->
	<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
		<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
		<property name="minPoolSize" value="${jdbc.minPoolSize}"></property>
	</bean>
	
	<!-- 2、配置JdbcTemplate操作数据库(pass) -->
	<!-- 3、配置使用mybatis操作数据库 -->
	<!-- 可以根据配置文件得到SqlSessionFactory -->
	<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
		
		<property name="dataSource" ref="ds"></property>
		<!-- 指定xml映射文件的位置 -->
		<property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"></property>
		
		<!-- 指定配置文件位置 -->
		<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property>
	</bean>
	<!-- 我们要把每一个dao接口的实现加入到ioc容器 -->
	<!-- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		指定dao接口所在的包
		<property name="basePackage" value="com.lql.dao"></property>
	</bean> -->
	<!-- 替换上面的简单写法 -->
	<mybatis-spring:scan base-package="com.lql.dao"/>
	
	<!-- 4、配置事务控制(配置事务管理器,让他控制住数据源里面的连接的关闭和提交) -->
	<bean id="tm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="ds"></property>
	</bean>
	
	<!-- 5、基于xml配置,配置事务(哪些方法切入事务,还要写切入点表达式) -->
	<aop:config>
		<!-- 配置切入点表达式 -->
		<aop:pointcut expression="execution(* com.lql.service.*.*(..))" id="txPoint"/>
		<aop:advisor advice-ref="myTx" pointcut-ref="txPoint"/>
	</aop:config>
	
	<!-- 6、配置事务增强、事务属性、事务建议
	transaction-manager="tm":指定要配置的事务管理器的id
	 -->
	<tx:advice id="myTx" transaction-manager="tm">
		<!-- 配置事务属性 -->
		<tx:attributes>
			<tx:method name="*" rollback-for="java.lang.Exception"/>
			<tx:method name="get*" read-only="true"/>
			<!-- <tx:method name="insertEmp" isolation="READ_UNCOMMITTED"/> -->
		</tx:attributes>
	</tx:advice>

</beans>


2.springmvc的配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
		
	<!-- SpringMVC只扫描控制器;禁用默认的规则 -->
	<context:component-scan base-package="com.lql" use-default-filters="false">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>
	
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/pages"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
	
	<!-- 配置文件上传解析器 -->
	<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<property name="defaultEncoding" value="utf-8"></property>
		<property name="maxUploadSize" value="#{1024*1024*20}"></property>
	</bean>
	
	<!-- 扫描静态资源 -->
	<mvc:default-servlet-handler/>
	<!-- 扫描动态资源 -->
	<mvc:annotation-driven></mvc:annotation-driven>
</beans>


3.mybatis的配置

<!-- 2、配置JdbcTemplate操作数据库(pass) -->
<!-- 3、配置使用mybatis操作数据库 -->
<!-- 可以根据配置文件得到SqlSessionFactory -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 指定配置文件位置 -->
    <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property>
    <property name="dataSource" ref="ds"></property>
    <!-- 指定xml映射文件的位置 -->
    <property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"></property>
</bean>
<!-- 我们要把每一个dao接口的实现加入到ioc容器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!-- 指定dao接口所在的包 -->
    <property name="basePackage" value="com.lql.dao"></property>
</bean>

mybatis-config.xml

<?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>

	<settings>
		<!-- 开启延迟加载开关 -->
		<setting name="lazyLoadingEnabled" value="true"/>
		<!-- 开启属性按需加载(默认开启) -->
		<!-- <setting name="aggressiveLazyLoading" value="false"/> -->
		<!-- 开启全局缓存开关(默认开启) -->
		<setting name="cacheEnabled" value="true"/>
	</settings>
	
    <!-- 配置分页插件 -->
	<plugins>
		<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
	</plugins>
  
</configuration>


3、测试(分页功能)

1.controller

package com.lql.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.lql.bean.Teacher;
import com.lql.service.TeacherService;

@Controller
public class TeacherController {

	@Autowired
	TeacherService teacherService;
	
	@RequestMapping("/getTea")
	public String getTeacher(@RequestParam(value="id",defaultValue="1")Integer id,Model model){
		Teacher t = teacherService.getTeacher(id);
		model.addAttribute("teacher", t);
		return "success";
	}
	
	@RequestMapping("/getAll")
	public String getAll(@RequestParam(value="pn",defaultValue="1")Integer pn,Model model){
		//紧跟他的查询就是一个分页查询
		PageHelper.startPage(pn, 5);
		List<Teacher> list = teacherService.getAll();
		
		//将查询的结果放在pageinfo中这个pageInfo就有非常多能够用的
		//第二份传入连续要显示的页码
		PageInfo<Teacher> pageInfo = new PageInfo<>(list,6);
		System.out.println("当前页码:"+pageInfo.getPageNum());
		System.out.println("总页码:"+pageInfo.getPages());
		System.out.println("总记录数:"+pageInfo.getTotal());
		System.out.println("当前页有几条记录:"+pageInfo.getSize());
		System.out.println("当前页的pageSize:"+pageInfo.getPageSize());
		System.out.println("前一页:"+pageInfo.getPrePage());
		System.out.println("结果:"+pageInfo.getList());//查询结果
		int[] nums = pageInfo.getNavigatepageNums();
		System.out.println(nums);
		model.addAttribute("pageInfo",pageInfo);
		return "success";
	}
}


2.success.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>成功!</h1>
${teacher }
<table cellpadding="5" cellspacing="0" border="1">
	<tr>
		<th>id</th>
		<th>name</th>
		<th>course</th>
		<th>address</th>
	</tr>
	<c:forEach items="${pageInfo.list}" var="tea">
		<tr>
			<td>${tea.id}</td>
			<td>${tea.name}</td>
			<td>${tea.course}</td>
			<td>新城区</td>
		</tr>
	</c:forEach>
	<tr>
		<td colspan="4">
			<a href="getAll?pn=1">首页</a><a href="getAll?pn=${pageInfo.prePage }">上一页</a>  
			
			<c:forEach items="${pageInfo.navigatepageNums}" var="num">
				<c:if test="${num == pageInfo.pageNum}">
					【${num}】
				</c:if>
				
				<c:if test="${num != pageInfo.pageNum}">
					<a href="getAll?pn=${num}">${num}</a>
				</c:if>
				
				
			</c:forEach>
			
			<a href="getAll?pn=${pageInfo.nextPage}">下一页</a><a href="getAll?pn=${pageInfo.pages}">末页</a>
		</td>
	</tr>
</table>

</body>
</html>

4、工程结构:

image-20201221134106225


八、MBG

  • MyBatis Generator(代码生成器)
  • MyBatis官方提供
  • 帮我们逆向生成

1、正向

  • table—》JavaBean—-》BookDao—-》dao.xml—-》xxx

2、逆向工程

  • 根据数据表table,逆向分析数据表,自动生成 JavaBean—-》BookDao—-》dao.xml—-》xxx

3、配置步骤

1.导包

  • mbg的核心包

mybatis-generator-core-1.4.0.jar


2.配置

  • 新建xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

  <!-- MyBatis3Simple:简单版 -->
  <context id="DB2Tables" targetRuntime="MyBatis3">
 	 <!-- jdbcConnection:指导连接到哪个数据库 -->
    <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
        connectionURL="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"
        userId="root"
        password="123456">
    </jdbcConnection>

    <javaTypeResolver >
      <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>
	
	<!-- javaModelGenerator:生成pojo
	targetPackage:生成的pojo放在哪个包
	targetProject:放在哪个工程下
	 -->
    <javaModelGenerator targetPackage="com.lql.bean" targetProject=".\src">
      <property name="enableSubPackages" value="true" />
      <property name="trimStrings" value="true" />
    </javaModelGenerator>
	
	<!-- sqlMapGenerator:sql映射文件生成器(指定xml生成的地方) -->
    <sqlMapGenerator targetPackage="com.lql.dao"  targetProject=".\conf">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>
	
	<!-- javaClientGenerator:生成dao接口 -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.lql.dao"  targetProject=".\src">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>
	
	<!-- table:指定要逆向生成哪个数据表
	tableName="t_cat":表名
	domainObjectName="Cat":这个表对应的对象名
	 -->
    <table tableName="t_cat" domainObjectName="Cat" ></table>
    <table tableName="employee" domainObjectName="Employee" ></table>
    <table tableName="t_teacher" domainObjectName="Teacher" ></table>

  </context>
</generatorConfiguration>

3.mbg生成代码

package com.lql.test;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;

public class MBGTest {

	public static void main(String[] args) throws Exception {
		List<String> warnings = new ArrayList<String>();
		boolean overwrite = true;
		File configFile = new File("mbg.xml");
		ConfigurationParser cp = new ConfigurationParser(warnings);
		Configuration config = cp.parseConfiguration(configFile);
		DefaultShellCallback callback = new DefaultShellCallback(overwrite);
		MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
		//代码生成
		myBatisGenerator.generate(null);
		System.out.println("生成🆗了!");
	}

}



==注:与mybatis相关的配置jar包,官方文档等都可以到GitHub中查找==