Java注解

* 概念:说明程序的。给计算机看的
* 注释:用文字描述程序的。给程序员看的

* 定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
* 概念描述:
    * JDK1.5之后的新特性
    * 说明程序的
    * 使用注解:@注解名称


​ * 作用分类:
​ ①编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
​ ②代码分析:通过代码里标识的注解对代码进行分析【使用反射】
​ ③编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】

* JDK中预定义的一些注解
    * @Override    :检测被该注解标注的方法是否是继承自父类(接口)的
    * @Deprecated:该注解标注的内容,表示已过时
    * @SuppressWarnings:压制警告
        * 一般传递参数all  @SuppressWarnings("all")

* 自定义注解
    * 格式:
        元注解
        public @interface 注解名称{
            属性列表;
        }

    * 本质:注解本质上就是一个接口,该接口默认继承Annotation接口
        * public interface MyAnno extends java.lang.annotation.Annotation {}

    * 属性:接口中的抽象方法
        * 要求:
            1. 属性的返回值类型有下列取值
                * 基本数据类型
                * String
                * 枚举
                * 注解
                * 以上类型的数组

            2. 定义了属性,在使用时需要给属性赋值
                1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
                2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
                3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略

    * 元注解:用于描述注解的注解
        * @Target:描述注解能够作用的位置
            * ElementType取值:
                * TYPE:可以作用于类上
                * METHOD:可以作用于方法上
                * FIELD:可以作用于成员变量上
        * @Retention:描述注解被保留的阶段
            * @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
        * @Documented:描述注解是否被抽取到api文档中
        * @Inherited:描述注解是否被子类继承


* 在程序使用(解析)注解:获取注解中定义的属性值
    1. 获取注解定义的位置的对象  (Class,Method,Field)
    2. 获取指定的注解
        * getAnnotation(Class)
        //其实就是在内存中生成了一个该注解接口的子类实现对象

                public class ProImpl implements Pro{
                    public String className(){
                        return "cn.itcast.annotation.Demo1";
                    }
                    public String methodName(){
                        return "show";
                    }
                }
    3. 调用注解中的抽象方法获取配置的属性值


* 案例:简单的测试框架
* 小结:
    1. 以后大多数时候,我们会使用注解,而不是自定义注解
    2. 注解给谁用?
        1. 编译器
        2. 给解析程序用
    3. 注解不是程序的一部分,可以理解为注解就是一个标签

待续~

Java反射:框架设计的灵魂

* * 框架:半成品软件。可以在框架的基础上进行软件开发,简化编码
* 反射:将类的各个组成部分封装为其他对象,这就是反射机制
    * 好处:
        1. 可以在程序运行过程中,操作这些对象。
        2. 可以解耦,提高程序的可扩展性。


* 获取Class对象的方式:
    1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
        * 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
    2. 类名.class:通过类名的属性class获取
        * 多用于参数的传递
    3. 对象.getClass():getClass()方法在Object类中定义着。
        * 多用于对象的获取字节码的方式

    * 结论:
        同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
* Class对象功能:
* 获取功能:
1. 获取成员变量们
* Field[] getFields() :获取所有public修饰的成员变量
* Field getField(String name) 获取指定名称的 public修饰的成员变量

* Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
* Field getDeclaredField(String name)
2. 获取构造方法们
* Constructor<?>[] getConstructors()
* Constructor<T> getConstructor(类<?>... parameterTypes)

* Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
* Constructor<?>[] getDeclaredConstructors()
3. 获取成员方法们:
* Method[] getMethods()
* Method getMethod(String name, 类<?>... parameterTypes)

* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name, 类<?>... parameterTypes)

4. 获取全类名
* String getName()
* Field:成员变量
    * 操作:
        1. 设置值
            * void set(Object obj, Object value)  
        2. 获取值
            * get(Object obj) 

        3. 忽略访问权限修饰符的安全检查
            * setAccessible(true):暴力反射



* Constructor:构造方法
    * 创建对象:
        * T newInstance(Object... initargs)  

        * 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法


* Method:方法对象
    * 执行方法:
        * Object invoke(Object obj, Object... args)  

    * 获取方法名称:
        * String getName:获取方法名


* 案例:
    * 需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
        * 实现:
            1. 配置文件
            2. 反射
        * 步骤:
            1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
            2. 在程序中加载读取配置文件
            3. 使用反射技术来加载类文件进内存
            4. 创建对象
            5. 执行方法

Spring学习笔记01-IOC和DI

Spring概述

spring 是什么

Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control: 反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多 著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。

spring 的体系结构

image-20200201222411257

IoC 的概念和作用

程序的耦合

耦合:程序间的依赖关系
包括:
类之间的依赖
方法间的依赖
解耦:降低程序间的依赖关系
实际开发中:
应该做到:编译期不依赖,运行时才依赖。

解耦的思路:
第一步:使用反射来创建对象,而避免使用new关键字。
第二步:通过读取配置文件来获取要创建的对象全限定类名

工厂模式解耦

image-20200201223124624

1
2
3
4
5
6
7
8
//被创建的对象 OUT.java
public class OUT {
public void out()
{
System.out.println("执行了dao的输出~");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//工厂 factory.java
public class factory {
private static Properties props;
private static Map<String,Object> beans;
static {
try {
//获取配置文件
props = new Properties();
InputStream is = factory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(is);
//实例化容器
beans = new HashMap<String, Object>();
//取出配置文件中所有的key
Enumeration<Object> keys = props.keys();
//遍历,创建对象存入map
while (keys.hasMoreElements())
{
//获取key
String key = keys.nextElement().toString();
//由key取类路径创建对象
Object value = Class.forName(props.getProperty(key)).newInstance();
beans.put(key,value);
}
}catch (Exception e)
{
System.out.println("初始化失败,读取配置错误");
}
}

public static Object getBean(String beanName)
{
return beans.get(beanName);
}
}
1
2
3
4
5
6
7
8
//TEST
public class test {
public static void main(String[] args) {
OUT out = (OUT) factory.getBean("accountDao");
out.out();
}
}

结果将会输出:执行了dao的输出~

控制反转-Inversion Of Control (IOC)

image-20200201223758622

image-20200201223736686

使用 spring的 IOC解决程序耦合

*特别说明: spring5 版本是用 jdk8 编写的,所以要求我们的 jdk版本是 8及以上。 同时 tomcat 的版本要求 8.5及以上。 *

基于 XML 的配置入门

第一步:拷贝必备的 jar包到工程的 lib 目录中 (导包)

第二步:在类的根路径下创建一个任意名称的 xml 文件(不能是中
文)

image-20200201224043724

第三步:让 spring 管理资源,在配置文件中配置 service 和 dao

image-20200201224124752

简单的使用案例

1
2
3
4
5
6
7
8
public static void main(String[] args) {   
//1.使用 ApplicationContext 接口,就是在获取 spring 容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.根据 bean 的 id 获取对象
IAccountService aService =(IAccountService)ac.getBean("accountService");
System.out.println(aService);
IAccountDao aDao = (IAccountDao)ac.getBean("accountDao"); System.out.println(aDao);
}

Spring基于 XML 的 IOC 细节

image-20200201224535017

image-20200201224607946

*BeanFactory和 ApplicationContext 的区别 *

image-20200201224658046

*ApplicationContext 接口的实现类 *

image-20200201224856375

IOC 中 bean 标签和管理对象细节

bean 标签

作用:

用于配置对象让 spring 来创建的。  
默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。 
属性:  

id:给对象在容器中提供一个唯一标识。用于获取对象。
class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
scope:指定对象的作用范围。

singleton :默认值,单例的.    
prototype :多例的.    
request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中. 
session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
global session :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么 
globalSession 相当于 session. 

init-method:指定类中的初始化方法名称。
destroy-method:指定类中销毁方法名称。

bean 的作用范围和生命周期

单例对象:scope=”singleton”
一个应用只有一个对象的实例。它的作用范围就是整个引用。
生命周期:
对象出生:当应用加载,创建容器时,对象就被创建了。
对象活着:只要容器在,对象一直活着。
对象死亡:当应用卸载,销毁容器时,对象就被销毁了。

多例对象:scope=”prototype”
每次访问对象时,都会重新创建对象实例。
生命周期:
对象出生:当使用对象时,创建新的对象实例。对象活着:只要对象在使用中,就一直活着。
对象死亡:当对象长时间不用时,被ava的垃圾回收器回收了。

实例化 Bean 的三种方式

第一种方式:使用默认无参构造函数

<!--在默认情况下:
1
2
<!--它会根据默认无参构造函数来创建类对象。如果bean中没有默认无参构造函数,将会创建失败。-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"/>

第二种方式:spring管理静态工厂-使用静态工厂的方法创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
*模拟一个静态工厂,创建业务层实现类
*/
public class StaticFactory{
public static IAccountService createAccountservice(){
return new AccountserviceImpl());
}
}
<!--
此种方式是:使用staticFactory类中的静态方法createAccountservice 创建对象,并存入spring 容器
id属性:指定bean的id,用于从容器中获取
class属性:指定静态工厂的全限定类名
factory-method属性:指定生产对象的静态方法
-->

<bean id="accountService” class="com.itheima.factory.staticFactory"
factory-method="createAccountservice></bean>

第三种方式:spring管理实例工厂-使用实例工厂的方法创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
*模拟一个实例工厂,创建业务层实现类
*此工厂创建对象,必须现有工厂实例对象,再调用方法
**/
public class InstanceFactory{
public IAccountService createAccountService(){
return new AccountServiceImpl();
}
}
<!--此种方式是:
先把工厂的创建交给spring来管理。然后在使用工厂的bean来调用里面的方法
factory-bean属性:用于指定实例工厂bean的id。
factory-method属性:用于指定实例工厂中创建对象的方法。
-->

<bean id="instancFactory"class="com.itheima.factory.InstanceFactory"></bean>
<bean id="accountService"
factory-bean="instanoFactory"
factory-method="createAccountService"></bean>

spring 的依赖注入

### 依赖注入的概念

依赖注入:Dependency Injection。它是apring框架核心ioc的具体实现。
我们的程序在编写时,通过控制反转,把对象的创建交给了apring,但是代码中不可能出现没有依赖的情况。
ioc解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。那这种业务层和持久层的依赖关系,在使用apring之后,就让apring 来维护了。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

构造函数注入

顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置
的方式,让 spring 框架来为我们注入。具体代码如下:

使用构造函数的方式,给service中的属性传值要求:
类中需要提供一个对应参数列表的构造函数。
涉及的标签:
constructor-arg

属性:
    index:指定参数在构造函数参数列表的索引位置type:指定参数在构造函数中的数据类型
    type:指定参数在构造函数中的数据类型 
    name:指定参数在构造函数中的名称     用这个找给谁赋值

=======上面三个都是找给谁赋值,下面两个指的是赋什么值的==============

value:它能赋的值是基本数据类型和 String 类型     
ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean   
1
2
3
4
5
6
7
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" >
<constructor-arg name="name" value="张三></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>

<bean id="now"class="java.util.Date"></bean>

set 方法注入

*顾名思义,就是在类中提供需要注入成员的 set 方法。具体代码如下: *

通过配置文件给 bean 中的属性传值:使用 set 方法的方式 涉及的标签:

property

属性:    
name:找的是类中 set 方法后面的部分    
ref:给属性赋值是其他 bean 类型的    
value:给属性赋值是基本数据类型和 string 类型的 
实际开发中,此种方式用的较多。 
1
2
3
4
5
6
7
<bean id="accountService" class="com.itheima.service.impl.AccountserviceImpl">
<property name="name" value="test"></property>
<property name="age" value="21"></property>
<property name="birthday" ref="now"></property>
</bean>

<bean id="now" class="java.util.Date "></bean>

使用 p 名称空间注入数据(本质还是调用 set 方法)

image-20200201231955300
image-20200201232010277

注入集合属性

顾名思义,就是给类中的集合成员传值,它用的也是set方法注入的方式,只不过变量的数据类型都是集合。 我们这里介绍注入数组,List,Set,Map,Properties。具体代码如下:

image-20200201232139074
image-20200201232152838
image-20200201232205287
image-20200201232216145

Mybatis缓存/逆向工程/插件扩展

MyBatis-缓存机制

• MyBatis 包含一个非常强大的查询缓存特性,它可以非 常方便地配置和定制。缓存可以极大的提升查询效率。
• MyBatis系统中默认定义了两级缓存。

• 一级缓存和二级缓存。

– 1、默认情况下,只有一级缓存(SqlSession级别的缓存, 也称为本地缓存)开启。

– 2、二级缓存需要手动开启和配置,他是基于namespace级 别的缓存。

– 3、为了提高扩展性。MyBatis定义了缓存接口Cache。我们 可以通过实现Cache接口来自定义二级缓存

一级缓存

• 一级缓存(local cache), 即本地缓存, 作用域默认 为sqlSession。当 Session flush 或 close 后, 该 Session 中的所有 Cache 将被清空。

• 本地缓存不能被关闭, 但可以调用 clearCache() 来清空本地缓存, 或者改变缓存的作用域.

• 在mybatis3.1之后, 可以配置本地缓存的作用域. 在 mybatis.xml 中配置

image-20200118202629944

一级缓存演示&失效情况

• 同一次会话期间只要查询过的数据都会保存在当 前SqlSession的一个Map中

• key:hashCode+查询的SqlId+编写的sql查询语句+参数
• 一级缓存失效的四种情况

– 1、不同的SqlSession对应不同的一级缓存

– 2、同一个SqlSession但是查询条件不同

– 3、同一个SqlSession两次查询期间执行了任何一次增 删改操作

– 4、同一个SqlSession两次查询期间手动清空了缓存

二级缓存

• 二级缓存(second level cache),全局作用域缓存
• 二级缓存默认不开启,需要手动配置
• MyBatis提供二级缓存的接口以及实现,缓存实现要求 POJO实现Serializable接口
• 二级缓存在 SqlSession 关闭或提交之后才会生效
• 使用步骤
– 1、全局配置文件中开启二级缓存

– 2、需要使用二级缓存的映射文件处使用cache配置缓存
3、注意:POJO需要实现Serializable接口

缓存相关属性

• eviction=“FIFO”:缓存回收策略:
• LRU – 最近最少使用的:移除最长时间不被使用的对象。
• FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
• SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
• WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
• 默认的是 LRU。
• flushInterval:刷新间隔,单位毫秒
• 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
• size:引用数目,正整数
• 代表缓存最多可以存储多少个对象,太大容易导致内存溢出
• readOnly:只读,true/false
• true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象 不能被修改。这提供了很重要的性能优势。
• false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些, 但是安全,因此默认是false。

缓存有关设置

• 1、全局setting的cacheEnable:
– 配置二级缓存的开关。一级缓存一直是打开的。
• 2、select标签的useCache属性:
– 配置这个select是否使用二级缓存。
一级缓存一直是使用的
• 3、sql标签的flushCache属性:
– 增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。 查询默认flushCache=false。
• 4、sqlSession.clearCache(): – 只是用来清除一级缓存。
• 5、当在某一个作用域 (一级缓存Session/二级缓存 Namespaces) 进行了 C/U/D 操作后,默认该作用域下所 有 select 中的缓存将被clear。

更多待记~

Mybatis动态Sql

动态 SQL是MyBatis强大特性之一。极大的简化我们拼装 SQL的操作。

• 动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处 理器相似。

• MyBatis 采用功能强大的基于 OGNL 的表达式来简化操作。

– if – choose (when, otherwise) 
– trim (where, set) 
– foreach

if

image-20200116164957914

choose (when, otherwise)

image-20200116165030751

trim (where, set)

where

image-20200116165101520

set

image-20200116165128304

trim

image-20200116165156555

foreach

• 动态 SQL 的另外一个常用的必要操作是需要对一个集合 进行遍历,通常是在构建 IN 条件语句的时候

image-20200116165238329

• 当迭代列表、集合等可迭代对象或者数组时 – index是当前迭代的次数,item的值是本次迭代获取的元素 • 当使用字典(或者Map.Entry对象的集合)时 – index是键,item是值

bind

• bind 元素可以从 OGNL 表达式中创建一个变量并 将其绑定到上下文。比如:

image-20200116165419880

Multi-db vendor support

• 若在 mybatis 配置文件中配置了 databaseIdProvider , 则可 以使用 “_databaseId”变量,这样就可以根据不同的数据库 厂商构建特定的语句

image-20200116165445085

image-20200116165458022

OGNL( Object Graph Navigation Language )对象图导航语言,这是一种强大的 表达式语言,通过它可以非常方便的来操作对象属性。 类似于我们的EL,SpEL等

访问对象属性:        person.name 
调用方法:           person.getName() 
调用静态属性/方法: @java.lang.Math@PI @java.util.UUID@randomUUID() 
调用构造方法:     new com.atguigu.bean.Person(‘admin’).name 
运算符:           +,-*,/,% 
逻辑运算符:         in,not in,>,>=,<,<=,==,!= 注意:xml中特殊符号如”,>,<等这些都需要使用转义字符

访问集合伪属性:

image-20200116165927364

Mybatis映射文件配置使用

MyBatis-映射文件

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

•cache             –命名空间的二级缓存配置 
•cache-ref         – 其他命名空间缓存配置的引用。 
•resultMap         – 自定义结果集映射
*parameterMap     – 已废弃!老式风格的参数映射 
•sql             –抽取可重用语句块。 
•insert             – 映射插入语句 
•update             – 映射更新语句
•delete         – 映射删除语句
•select             – 映射查询语句

## insert、update、delete元素

image-20200115130131842

细节

*1、mybatis允许增删改直接在接口方法定义以下类型返回值
Integer、Long、Boolean、void-等基本类型
*

2、我们需要手动提交数据
sqlSessionFactory.openSession();===》手动提交
sqlSessionFactory.openSession(true);===》自动提交

主键生成方式

若数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),则可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上。

mysql支持自增主键,自增主键值的获取,mybatis也是利用statement.getGenreatedKeys();useGeneratedKeys=”true”;使用自增主键获取主键值策略keyProperty;指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给javaBean的哪个属性

image-20200115130245040

而对于不支持自增型主键的数据库(例如 Oracle),则可以使用 selectKey 子元素: selectKey 元素将会首先运行,id 会被设置,然 后插入语句会被调用

keyProperty:查出的主键值封装给javaBean的哪个属性
order=”BEFORE”:当前sql在插入sql之前运行
AFTER:当前sql在插入sql之后运行
resultType:查出的数据的返回值类型

BEFORE运行顺序:
    先运行selectKey查询id的sql;查出id值封装给javaBean的id属性
    在运行插入的sql;就可以取出id属性对应的值
AFTER运行顺序:
    先运行插入的sql(从序列中取出新值作为id);
    再运行selectKey查询id的sql;

image-20200115130339089

image-20200115130352566

参数(Parameters)传递

单个参数

– 可以接受基本类型,对象类型,集合类型的值。这种情况 MyBatis可直接使用这个参数,不需要经过任何处理。

*• 多个参数 *

– 任意多个参数,都会被MyBatis重新包装成一个Map传入。 Map的key是param1,param2,0,1…,值就是参数的值。 

• 命名参数

– 在接口中为参数使用@Param起一个名字,MyBatis就会将这些参数封 装进map中,key就是我们自己指定的名字 

• POJO

– 当这些参数属于我们业务POJO时,我们直接传递POJO 

• Map

– 我们也可以封装多个参数为map,直接传递

##特别注意:

如果是Collection(List、Set)类型或者是数组,也会特殊处理。也是把传入的list或者数组封装在map中。
key:collection(collection),如果是List还可以使用这个key(list)
数组(array)

public Employee getEmpById(List ids);取值:取出第一-个id的值:#{list[0]}

参数值的获取

select * from tbl_employee where id=${id} and last_name=#{lastName}
Preparing: select * from tbl_employee where id=2 and last_name=?
区别:
    #{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入
    ${}:取出的值直接拼装在sql语句中;会有安全问题;
    大多情况下,我们去参数的值都应该去使用#{};

原生jdbc不支持占位符的地方我们就可以使用${}进行取值
比如分表、排序。。。;按照年份分表拆分
select * from ${year}_salary where xxx;
select * from tbl_employee order by ${f_name} ${order}
原生JDBC不支持预编译的需要使用$

#{}:更丰富的用法:
规定参数的一些规则:
javaType、 jdbcType、 mode(存储过程)、 numericScale、
resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能);

jdbcType通常需要在某种特定的条件下被设置:
    在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);

    JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理;

    由于全局配置中:jdbcTypeForNull=OTHER;oracle不支持;两种办法
    1、#{email,jdbcType=OTHER};
    2、jdbcTypeForNull=NULL
        全局配置:<setting name="jdbcTypeForNull" value="NULL"/>

参数处理

• 参数也可以指定一个特殊的数据类型:

image-20200115134010614

– javaType 通常可以从参数对象中来去确定

– 如果 null 被当作值来传递,对于所有可能为空的列, jdbcType 需要被设置

– 对于数值类型,还可以设置小数点后保留的位数

– mode 属性允许指定 IN,OUT 或 INOUT 参数。如果参数 为 OUT 或 INOUT,参数对象属性的真实值将会被改变, 就像在获取输出参数时所期望的那样。

• 参数位置支持的属性

– javaType、jdbcType、mode、numericScale、 resultMap、typeHandler、jdbcTypeName、expression

• 实际上通常被设置的是: 可能为空的列名指定 jdbcType

• #{key}:获取参数的值,预编译到SQL中。安全。

• ${key}:获取参数的值,拼接到SQL中。有SQL注入问 题。ORDER BY ${name}

select元素

• Select元素来定义查询操作。

• Id:唯一标识符。

– 用来引用这条语句,需要和接口的方法名一致

• parameterType:参数类型。

– 可以不传,MyBatis会根据TypeHandler自动推断

• resultType:返回值类型。

– 别名或者全类名,如果返回的是集合,定义集合中元 素的类型。不能和resultMap同时使用

image-20200115143200639

image-20200115134256703

自动映射

• 1、全局setting设置

– autoMappingBehavior默认是PARTIAL,开启自动映射 的功能。唯一的要求是列名和javaBean属性名一致 
– 如果autoMappingBehavior设置为null则会取消自动映射 
– 数据库字段命名规范,POJO属性符合驼峰命名法,如 A_COLUMNaColumn,我们可以开启自动驼峰命名规 则映射功能,mapUnderscoreToCamelCase=true。

• 2、自定义resultMap,实现高级结果集映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
<?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.atguigu.mybatis.dao.EmployeeMapperPlus">

<!--自定义某个javaBean的封装规则
type:自定义规则的Java类型
id:唯一id方便引用
-->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MySimpleEmp">
<!--指定主键列的封装规则
id定义主键会底层有优化;
column:指定哪一列
property:指定对应的javaBean属性
-->
<id column="id" property="id"/>
<!-- 定义普通列封装规则 -->
<result column="last_name" property="lastName"/>
<!-- 其他不指定的列会自动封装:我们只要写resultMap就把全部的映射规则都写上。 -->
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</resultMap>

<!-- resultMap:自定义结果集映射规则; -->
<!-- public Employee getEmpById(Integer id); -->
<select id="getEmpById" resultMap="MySimpleEmp">
select * from tbl_employee where id=#{id}
</select>

<!--
场景一:
查询Employee的同时查询员工对应的部门
Employee===Department
一个员工有与之对应的部门信息;
id last_name gender d_id did dept_name (private Department dept;)
-->


<!--
联合查询:级联属性封装结果集
-->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="did" property="dept.id"/>
<result column="dept_name" property="dept.departmentName"/>
</resultMap>


<!--
使用association定义关联的单个对象的封装规则;
-->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp2">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>

<!-- association可以指定联合的javaBean对象
property="dept":指定哪个属性是联合的对象
javaType:指定这个属性对象的类型[不能省略]
-->
<association property="dept" javaType="com.atguigu.mybatis.bean.Department">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
</association>
</resultMap>
<!-- public Employee getEmpAndDept(Integer id);-->
<select id="getEmpAndDept" resultMap="MyDifEmp">
SELECT e.id id,e.last_name last_name,e.gender gender,e.d_id d_id,
d.id did,d.dept_name dept_name FROM tbl_employee e,tbl_dept d
WHERE e.d_id=d.id AND e.id=#{id}
</select>

<!-- 使用association进行分步查询:
1、先按照员工id查询员工信息
2、根据查询员工信息中的d_id值去部门表查出部门信息
3、部门设置到员工中;
-->

<!-- id last_name email gender d_id -->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpByStep">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!-- association定义关联对象的封装规则
select:表明当前属性是调用select指定的方法查出的结果
column:指定将哪一列的值传给这个方法

流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
-->
<association property="dept"
select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
column="d_id">
</association>
</resultMap>
<!-- public Employee getEmpByIdStep(Integer id);-->
<select id="getEmpByIdStep" resultMap="MyEmpByStep">
select * from tbl_employee where id=#{id}
<if test="_parameter!=null">
and 1=1
</if>
</select>

<!-- 可以使用延迟加载(懒加载);(按需加载)
Employee==>Dept:
我们每次查询Employee对象的时候,都将一起查询出来。
部门信息在我们使用的时候再去查询;
分段查询的基础之上加上两个配置:
-->
<!-- ==================association============================ -->

<!--
场景二:
查询部门的时候将部门对应的所有员工信息也查询出来:注释在DepartmentMapper.xml中
-->
<!-- public List<Employee> getEmpsByDeptId(Integer deptId); -->
<select id="getEmpsByDeptId" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee where d_id=#{deptId}
</select>



<!-- =======================鉴别器============================ -->
<!-- <discriminator javaType=""></discriminator>
鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
封装Employee:
如果查出的是女生:就把部门信息查询出来,否则不查询;
如果是男生,把last_name这一列的值赋值给email;
-->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpDis">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!--
column:指定判定的列名
javaType:列值对应的java类型 -->
<discriminator javaType="string" column="gender">
<!--女生 resultType:指定封装的结果类型;不能缺少。/resultMap-->
<case value="0" resultType="com.atguigu.mybatis.bean.Employee">
<association property="dept"
select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
column="d_id">
</association>
</case>
<!--男生 ;如果是男生,把last_name这一列的值赋值给email; -->
<case value="1" resultType="com.atguigu.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="last_name" property="email"/>
<result column="gender" property="gender"/>
</case>
</discriminator>
</resultMap>
</mapper>