mybatis入门

一、MyBatis概念

        MyBatis(半自动)是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或者注解来配置和映射原生类型、接口和Java的POJO(Plain Old Java Objects,普通老式Java对象)为数据库中的记录。

持久层框架 orm框架(object relation mapping)对象关系映射

javadatabase
对象一行数据(主键
属性

目前主流持久层框架:hibernate(全自动),jpa,springjdbc。

j2ee的三大框架:ssh(struts2+spring+hibernate)

ssm(SpringMVC/SpringBoot+Spring+MyBatis)

ssj(SpringMVC+Spring+Springjdbc)

hibernate和mybatis的区别:hibernate全自动 不需要写sql,缺点,不安全,银行系统不支持全自动。

mybatis半自动 需要写sql。

二、如何使用MyBatis

步骤一:pom.xml文件中引入jar包

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.5.6</version>
</dependency>

步骤二:配置全局配置文件mybatis-config.xml

每个基于MyBatis的应用都是以一个SqlSessionFactory的实例为核心的。SqlSessionFactory的实例可以通过SqlSessionFactoryBuilder获得。而SqlSessionFactoryBuilder则可以从XML配置文件或者一个预先配置的Configuration实例来构建出SqlSessionFactory实例。

 全局配置文件    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.jdbc.Driver"/>  
<!--                                                        自己数据库ip 端口号 自己数据库名称-->
                <property name="url" value="jdbc:mysql://localhost:3306/cloudwise?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false"/>
<!--                数据库用户名-->
                <property name="username" value="root"/> 
<!--                数据库密码-->
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!--    映射文件-->
    <mappers>
        <mapper resource="mapper/UserInfoMapper.xml"/>
    </mappers>
</configuration>

 第三步:构建 SqlSessionFactory

可以从配置或者直接编码来创建SqlSessionFactory 通过SqlSessionFactory创建SqlSession

package com.aaa.mybatis;

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 java.io.IOException;
import java.io.InputStream;

/**
 * @version 1.0
 * @author:张先生
 * @description: 我自己创建sqlsession的工具类
 * @date 2022/8/10 15:46
 */
public class MySqlSessionFactory {
    static String resource = "mybatis-config.xml";
    static SqlSessionFactory sqlSessionFactory;
    static {
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }
    /**
     * @create by: Teacher张
     * @description: 获取sqlsession
     * @create time: 2022/8/10 15:52
     * @param 
     * @return SqlSession
     */
    public static SqlSession getSQLSession(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        return sqlSession;
    }
}

步骤四:创建dao接口

public interface UserInfoMybatisDao {
    List<User> findAllUser();
}

步骤五:创建mapper映射文件

<?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.aaa.dao.UserMybatisDao">

    <select id="findAllUser" resultType="com.aaa.entity.User">
        select * from user
    </select>
</mapper>

 步骤六:调用session.commit()提交事务

如果是更新、添加、删除语句,我们还需要提交一下事务。

步骤七:调用session.close()关闭会话

最后一定要记得关闭会话。

安装mybatis插件

 步骤八:使用junit测试工具单元测试

1.导入junit的jar包

2.在dao接口中生成测试类(alt+insert  点击测试)

 

package com.aaa.dao;

import com.aaa.entity.User;
import com.aaa.mybatis.MySqlSessionFactory;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;


public class UserMybatisDaoTest {

    SqlSession sqlSession;

    @Before
    public void init() throws Exception {
      sqlSession = MySqlSessionFactory.getSQLSession();
    }

    @After
    public void aaa() throws Exception {
        sqlSession.commit();
        sqlSession.close();
    }

    @Test
    public void findAllUser() {
        UserMybatisDao mapper = sqlSession.getMapper(UserMybatisDao.class);
        List<User> allUser = mapper.findAllUser();
        for (User user : allUser) {
            System.out.println(user.toString());
        }
    }
}

步骤九:mybatis配置文件

1.properties

这些属性可以在外部进行配置,并可以进行动态替换。你既可以典型Java属性文件中配置这些属性,也可以在properties元素的子元素中设置。例如

修改配置 对应自己的数据库

2.settings

这是MyBatis中极为重要的调整设置,它们会改变MyBatis的运行时行为。下表描述了设置中各项的意图、默认值等  在xml文件中加上

    <settings>
<!--        关闭二级缓存-->
        <setting name="cacheEnabled" value="false"/>
<!--        开启日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

 测试一级缓存

public class UserMybatisDaoTest {

    SqlSession sqlSession;
    UserDao userDao;
    @Before
    //测试之前
    public void setUp() throws Exception {
        sqlSession = MySqlSessionFactory.getSQLSession();
        userDao  = sqlSession.getMapper(UserDao.class);
    }

    @After
    //测试之后
    public void tearDown() throws Exception {
        System.out.println("结束测试");
        if (sqlSession!=null){
            //事务提交
            sqlSession.commit();
        }else {
            //关闭sqlSession
            sqlSession.close();
        }
    }

/*    @Test
    public void findAllUser() {
        UserMybatisDao mapper = sqlSession.getMapper(UserMybatisDao.class);
        List<User> allUser = mapper.findAllUser();
        for (User user : allUser) {
            System.out.println(user.toString());
        }
    }*/

    @Test
/*
    mybatis的一级缓存,在同一个sqlsession中生效
    一旦sqlsession关闭,缓存消失,查询相同的内容,需要重新连接数据库获取
*/

    public void findAllUser(){
        // 从测试案例可以看出,同一次会话,默认缓存数据,第二次查询相同的数据,直接从缓存中获取
        System.out.println("开始第一次查询");
        List<User> userAll1 = userDao.findAllUser();
        for (User user : userAll1) {
            System.out.println("实体类"+user.toString());
        }

        System.out.println("第二次查询");
        List<User> userAll2 = userDao.findAllUser();
        for (User user : userAll2) {
            System.out.println("实体类"+user.toString());
        }

/*
        第三次关闭sqlsession,重新打开一个sqlsession
        测试案例可以发现,一旦sqlsession关闭,缓存自动消失,查询相同的内容,需要重新连接数据库获取。

*/
        //关闭sqlsession
        //sqlSession.close();
        //重新打开一个新的sqlsession
       /* SqlSession sqlSession1 = MySqlSessionFactory.getSQLSession();
        UserMybatisDao mapper = sqlSession1.getMapper(UserMybatisDao.class);*/
        System.out.println("第三次查询");
        List<User> userAll3 = userDao.findAllUser();
        for (User user : userAll3) {
            System.out.println("实体类"+user.toString());
        }
    }
}

 

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
开始第一次查询
Opening JDBC Connection
Created connection 1624820151.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@60d8c9b7]
==>  Preparing: select * from `user`
==> Parameters: 
<==    Columns: id, name, age, email
<==        Row: 1, Jone, 18, test1@baomidou.com
<==        Row: 2, Jack, 20, test2@baomidou.com
<==        Row: 3, Tom, 28, test3@baomidou.com
<==        Row: 4, Sandy, 21, test4@baomidou.com
<==        Row: 5, Billie, 24, test5@baomidou.com
<==      Total: 5
实体类User{id=1, name='Jone', age=18, email='test1@baomidou.com'}
实体类User{id=2, name='Jack', age=20, email='test2@baomidou.com'}
实体类User{id=3, name='Tom', age=28, email='test3@baomidou.com'}
实体类User{id=4, name='Sandy', age=21, email='test4@baomidou.com'}
实体类User{id=5, name='Billie', age=24, email='test5@baomidou.com'}
第二次查询
实体类User{id=1, name='Jone', age=18, email='test1@baomidou.com'}
实体类User{id=2, name='Jack', age=20, email='test2@baomidou.com'}
实体类User{id=3, name='Tom', age=28, email='test3@baomidou.com'}
实体类User{id=4, name='Sandy', age=21, email='test4@baomidou.com'}
实体类User{id=5, name='Billie', age=24, email='test5@baomidou.com'}
第三次查询
实体类User{id=1, name='Jone', age=18, email='test1@baomidou.com'}
实体类User{id=2, name='Jack', age=20, email='test2@baomidou.com'}
实体类User{id=3, name='Tom', age=28, email='test3@baomidou.com'}
实体类User{id=4, name='Sandy', age=21, email='test4@baomidou.com'}
实体类User{id=5, name='Billie', age=24, email='test5@baomidou.com'}
结束测试

Process finished with exit code 0

 从上面的测试案例可以看出,同一次会话,默认缓存数据,第二次查询相同的数据,直接从缓存中获取。

public class UserMybatisDaoTest {

    SqlSession sqlSession;
    UserDao userDao;
    @Before
    //测试之前
    public void setUp() throws Exception {
        sqlSession = MySqlSessionFactory.getSQLSession();
        userDao  = sqlSession.getMapper(UserDao.class);
    }

    @After
    //测试之后
    public void tearDown() throws Exception {
        System.out.println("结束测试");
        if (sqlSession!=null){
            //事务提交
            sqlSession.commit();
        }else {
            //关闭sqlSession
            sqlSession.close();
        }
    }

/*    @Test
    public void findAllUser() {
        UserMybatisDao mapper = sqlSession.getMapper(UserMybatisDao.class);
        List<User> allUser = mapper.findAllUser();
        for (User user : allUser) {
            System.out.println(user.toString());
        }
    }*/

    @Test
/*
    mybatis的一级缓存,在同一个sqlsession中生效
    一旦sqlsession关闭,缓存消失,查询相同的内容,需要重新连接数据库获取
*/

    public void findAllUser(){
        // 从测试案例可以看出,同一次会话,默认缓存数据,第二次查询相同的数据,直接从缓存中获取
        System.out.println("开始第一次查询");
        List<User> userAll1 = userDao.findAllUser();
        for (User user : userAll1) {
            System.out.println("实体类"+user.toString());
        }

        System.out.println("第二次查询");
        List<User> userAll2 = userDao.findAllUser();
        for (User user : userAll2) {
            System.out.println("实体类"+user.toString());
        }

/*
        第三次关闭sqlsession,重新打开一个sqlsession
        测试案例可以发现,一旦sqlsession关闭,缓存自动消失,查询相同的内容,需要重新连接数据库获取。

*/
        sqlSession.close();
        //重新打开一个新的sqlsession
        SqlSession sqlSession1 = MySqlSessionFactory.getSQLSession();
        UserDao mapper = sqlSession1.getMapper(UserDao.class);
        System.out.println("第三次查询");
        List<User> userAll3 = mapper.findAllUser();
        for (User user : userAll3) {
            System.out.println("实体类"+user.toString());
        }
    }
}

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
开始第一次查询
Opening JDBC Connection
Created connection 2035070981.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@794cb805]
==>  Preparing: select * from `user`
==> Parameters: 
<==    Columns: id, name, age, email
<==        Row: 1, Jone, 18, test1@baomidou.com
<==        Row: 2, Jack, 20, test2@baomidou.com
<==        Row: 3, Tom, 28, test3@baomidou.com
<==        Row: 4, Sandy, 21, test4@baomidou.com
<==        Row: 5, Billie, 24, test5@baomidou.com
<==      Total: 5
实体类User{id=1, name='Jone', age=18, email='test1@baomidou.com'}
实体类User{id=2, name='Jack', age=20, email='test2@baomidou.com'}
实体类User{id=3, name='Tom', age=28, email='test3@baomidou.com'}
实体类User{id=4, name='Sandy', age=21, email='test4@baomidou.com'}
实体类User{id=5, name='Billie', age=24, email='test5@baomidou.com'}
第二次查询
实体类User{id=1, name='Jone', age=18, email='test1@baomidou.com'}
实体类User{id=2, name='Jack', age=20, email='test2@baomidou.com'}
实体类User{id=3, name='Tom', age=28, email='test3@baomidou.com'}
实体类User{id=4, name='Sandy', age=21, email='test4@baomidou.com'}
实体类User{id=5, name='Billie', age=24, email='test5@baomidou.com'}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@794cb805]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@794cb805]
Returned connection 2035070981 to pool.
第三次查询
Opening JDBC Connection
Checked out connection 2035070981 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@794cb805]
==>  Preparing: select * from `user`
==> Parameters: 
<==    Columns: id, name, age, email
<==        Row: 1, Jone, 18, test1@baomidou.com
<==        Row: 2, Jack, 20, test2@baomidou.com
<==        Row: 3, Tom, 28, test3@baomidou.com
<==        Row: 4, Sandy, 21, test4@baomidou.com
<==        Row: 5, Billie, 24, test5@baomidou.com
<==      Total: 5
实体类User{id=1, name='Jone', age=18, email='test1@baomidou.com'}
实体类User{id=2, name='Jack', age=20, email='test2@baomidou.com'}
实体类User{id=3, name='Tom', age=28, email='test3@baomidou.com'}
实体类User{id=4, name='Sandy', age=21, email='test4@baomidou.com'}
实体类User{id=5, name='Billie', age=24, email='test5@baomidou.com'}
结束测试

从上面的测试案例可以发现,mybatis的一级缓存,在同一个sqlsession中生效,,一旦sqlsession关闭,缓存自动消失,查询相同的内容,需要重新连接数据库获取。

设置参数

描述

有效值

默认值

cacheEnabled

该配置影响的所有映射器中配置的缓存的全局开关。

true,false

true

lazyLoadingEnabled

懒加载,延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。

true,false

false

aggressiveLazyLoading

当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载。

true,false,true

multipleResultSetsEnabled

是否允许单一语句返回多结果集(需要兼容驱动)。

true,false

true

useColumnLabel

使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。

true,false

true

useGeneratedKeys

允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。

true,false

False

autoMappingBehavior

指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。

NONE, PARTIAL, FULL

PARTIAL

defaultExecutorType

配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。

SIMPLE REUSE BATCH

SIMPLE

defaultStatementTimeout

设置超时时间,它决定驱动等待数据库响应的秒数。

Any positive integer

Not Set (null)

safeRowBoundsEnabled

允许在嵌套语句中使用分页(RowBounds)。

true,false

False

mapUnderscoreToCamelCase

是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。

true, false

False

localCacheScope

MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。

SESSION,STATEMENT

SESSION

jdbcTypeForNull

当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。

JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER

OTHER

lazyLoadTriggerMethods

指定哪个对象的方法触发一次延迟加载。

A method name list separated by commas

equals,clone,hashCode,toString

defaultScriptingLanguage

指定动态 SQL 生成的默认语言。

A type alias or fully qualified class name.

org.apache.ibatis.scripting.xmltags.XMLDynamicLanguageDriver

callSettersOnNulls

指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null 的。

true,false

false

logPrefix

指定 MyBatis 增加到日志名称的前缀。

Any String

Not set

logImpl

指定 MyBatis 所用日志的具体实现,未指定时将自动查找。

SLF4J,LOG4J,LOG4J2,JDK_LOGGING,COMMONS_LOGGING,STDOUT_LOGGING,NO_LOGGING

Not set

proxyFactory

指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。

CGLIB JAVASSIST

CGLIB

vfslmpl

指定 VFS 的实现

自定义 VFS 的实现的类全限定名,以逗号分隔。

no set

useActualParamName

允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1)

true | false

true

configurationFactory

指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3)

类型别名或者全类名.

no set                     

3.typeAliases

类型别名是为Java类型设置一个短的名字。它只和XML配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:

在mabatis-config.xml配置

    <!--    设置别名-->
    <typeAliases>
        <typeAlias alias="aaa" type="com.aaa.entity.User"></typeAlias>
    </typeAliases>

 在mapper里那个userMapper.xml 里 resultType 写上别名

    <select id="findAllUser" resultType="aaa">
        select * from `user`
    </select>

4.mybatis关联查询

4.1.多个条件查询

 

org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: org.apache.ibatis.binding.BindingException: Parameter 'ename' not found. Available parameters are [arg1, arg0, param1, param2]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'ename' not found. Available parameters are [arg1, arg0, param1, param2]

 在使用多条件查询的时候,会出现上面的错误,以下有解决方案:

4.1.1 使用注解方式

/**
 * 按照部门的名称和地址查询
   #{}里面的名称对应的是注解@Param括号里面修饰的名称。
 */
List<User> findUserByNameAndAge(@Param("xxx") String name, @Param("yyy") String age);
<select id="findUserByNameAndAge" resultType="com.aaa.entity.User">
        select * from user where name=#{xxx} and age=#{yyy}
</select>

4.1.2 使用内置变量arg0方式

/**
 * 按照部门的名称和地址查询
 */
List<User> findUserByNameAndAge(String name,String age);
<select id="findUserByNameAndAge" resultType="com.aaa.entity.User">
        select * from user where name=#{arg0} and age=#{arg1}
</select>

4.1.3 使用map封装多个参数(或者使用实体类)

/**
 * 按照部门的名称和地址查询
   Map      #{}里面的名称对应的是Map里面的key名称     
   实体类    #{}里面的名称对应的是User类里面的成员属性
 */
List<Dept> findUserByNameAndAgeMap(Map map);
<select id="findUserByNameAndAgeMap" resultType="com.aaa.entity.User">
    select * from dept where name=#{name} and age=#{age}
</select>

5.resultType和resultMap的区别

如果数据库中的列名和java实体类中属性名完全一致,可以使用resultType。

如果数据库的列名和实体类的属性名不一致,或者复杂查询产生临时列,新属性,临时列和新属性的名字不一致,resultmap就必须要使用了。

jdbcType 和JavaType的对应关系

JDBC Type           Java Type
CHAR                String
VARCHAR             String
LONGVARCHAR         String
NUMERIC             java.math.BigDecimal
DECIMAL             java.math.BigDecimal
BIT                 boolean
BOOLEAN             boolean
TINYINT             byte
SMALLINT            short
INTEGER             INTEGER
BIGINT              long
REAL                float
FLOAT               double
DOUBLE              double
BINARY              byte[]
VARBINARY           byte[]
LONGVARBINARY       byte[]
DATE                java.sql.Date
TIME                java.sql.Time
TIMESTAMP           java.sql.Timestamp
CLOB                Clob
BLOB                Blob
ARRAY               Array
DISTINCT            mapping of underlying type
STRUCT              Struct
REF                 Ref
DATALINK            java.net.URL

 

5.1实体类属性名和表中字段名不一样 ,怎么办?

第1种:通过在查询的SQL语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。

<select id="getOrder" parameterType="int" resultType="com.jourwon.pojo.Order">
       select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
</select>

第2种:通过resultMap  中的<result>来映射字段名和实体类属性名的一一对应的关系。

<select id="getOrder" parameterType="int" resultMap="orderResultMap">
  select * from orders where order_id=#{id}
</select>
<resultMap type="com.jourwon.pojo.Order" id="orderResultMap">
    <!–用id属性来映射主键字段–>
    <id property="id" column="order_id">
    <!–用result属性来映射非主键字段,property为实体类属性名,column为数据库表中的属性–>
  <result property ="orderno" column ="order_no"/>
  <result property="price" column="order_price" />
</reslutMap>

5.2 关联查询 多对一查询

多方为主表,一方为附表    查询所有员工的详细信息,包括部门名称,部门地址。

select  e.*,d.dname,d.loc from emp e left join dept d on e.deptid = d.deptid 

emp 

private long empno;
private String ename;
private String job;
private long mgr;
private java.sql.Timestamp hiredate;
private double sal;
private double comm;
private Dept dept;

 dept

private long deptid;
private String dname;
private String loc;
<?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.cloudwise.dao.EmpDao">
    <resultMap id="empResultMap" type="com.cloudwise.entity.Emp" autoMapping="true">
<!-- 多对一-->
       <association property="dept" javaType="com.cloudwise.entity.Dept" column="deptid" >
            <id property="deptid" column="deptid"></id>
            <result property="dname" column="dname"></result>
            <result property="loc" column="loc"></result>
       </association>
    </resultMap>

    <select id="findAllEmp" resultMap="empResultMap">
        select  e.*,d.dname,d.loc from emp e left join dept d on e.deptid = d.deptid
    </select>
</mapper>

5.3 一对多查询

一个部门对应多个员工,需求,根据部门id查询单个部门的所有员工。

<!--    一对多关系-->
   <resultMap type="com.cloudwise.entity.Dept" id="DeptMap" autoMapping="true" >
    <id property="deptno" column="deptno" jdbcType="INTEGER"></id>
    <collection property="emps" ofType="com.cloudwise.entity.Emp"   autoMapping="true" >
    </collection>
</resultMap>
<!--查询单个-->
<select id="queryById"  resultMap="DeptMap">
    select d.* ,e.* from dept d left join emp  e on d.deptno=e.deptno where d.deptno=#{deptno}
</select>

测试结果

[DEBUG][com.cloudwise.dao.DeptDao.queryById] - ==>  Preparing: select d.* ,e.* from dept d left join emp e on d.deptno=e.deptno where d.deptno=?

[DEBUG][com.cloudwise.dao.DeptDao.queryById] - ==> Parameters: 10(Integer)

[DEBUG][com.cloudwise.dao.DeptDao.queryById] - <==      Total: 3

Dept(deptno=10, dname=财务部, loc=郑州, emps=[Emp(empno=7782, ename=CLARK, job=MANAGER, mgr=7839, hiredate=Tue Jun 09 00:00:00 CST 1981, sal=2450.0, comm=null, deptno=10), Emp(empno=7839, ename=KING, job=PRESIDENT, mgr=null, hiredate=Tue Nov 17 00:00:00 CST 1981, sal=14764.0, comm=null, deptno=10), Emp(empno=7934, ename=MILLER, job=CLERK, mgr=7782, hiredate=Sat Jan 23 00:00:00 CST 1982, sal=1300.0, comm=null, deptno=10)])

部门人数3

5.4 mybatis多态sql

sql语句根据传入参数的状态动态改变,条件查询,批量删除

5.4.1   if满足一个条件自动拼接一条

 

select * from dept where 1=1
<if test="dname!=null and dname!=''">
and dname=#{dname}
</if>
<if test="loc!=null and loc!=''">
    and loc=#{loc}
</if>

5.4.2   choose , when ,otherwise和Java 中的 switch case语句类似 只拼接一条

查询emp表,如果ename有值,按照enam查询,如果empno有值,按照empno查询

其他情况按照工资查询

<select id="findAllEmpByCondition" parameterType="com.cloudwise.entity.Emp" resultType="com.cloudwise.entity.Emp">
    select * from emp
    <where>
        <choose>
            <when test="ename!=null and ename!=''">
                and ename =#{ename}
            </when>
            <when test="empno!=null and empno!=''">
                and empno =#{empno}
            </when>
            <otherwise>
                <if test="sal!=null and sal !=''">
                    and sal=#{sal}
                </if>

            </otherwise>
        </choose>
    </where>


</select>

5.4.3  where

<select id="findAllDeptByCondition" parameterType="dept" resultType="com.cloudwise.entity.Dept">
    select * from dept
    <where>
        <if test="dname!=null and dname!=''">
            and dname=#{dname}
        </if>
        <if test="loc!=null and loc!=''">
            and loc=#{loc}
        </if>
    </where>
</select>

 解决方案,在xml文件中尽量避免写大于,小于,大于等于,小于等于,  &

 5.4.4 使用数据库自动生成的主键

<!--    使用数据库自动生成的主键-->
    <insert id="addDept" keyProperty="deptno" useGeneratedKeys="true">
        insert into  dept ( dname, loc) values (#{dname},#{loc})
    </insert>
    
@Test
public void addDept(){

        Dept dept= new Dept();
        dept.setDname("部门cc");
        dept.setLoc("位置cc");
    System.out.println("保存之前的dept:"+dept.toString());
    int i = deptDao.addDept(dept);
    System.out.println(i);
    System.out.println("保存之后的dept:"+dept.toString());
}

 5.4.5   #{} 和 ${}区别

#{}是占位符,预编译处理;   ${}是拼接符,字符串替换,没有预编译处理。
Mybatis在处理#{}时,#{}传入参数是以字符串传入,会将SQL中的#{}替换为?号,调用PreparedStatement的set方法来赋值。
#{} 可以有效的防止SQL注入,提高系统安全性;${}不能防止SQL注入
#{} 的变量替换是在DBMS中;${} 的变量替换是在DBMS外

5.4.6  模糊查询like语句

1 ’%${xxx}%’ 可能引起SQL注入,不推荐

2 "%"#{xxx}"%" 注意:因为#{…}解析成sql语句时候,会在变量外侧自动加单引号, 所以这里 % 需要使用双引号,不能使用单引号 ,不然会查不到任何结果。

3 concat('%',#{xxx},'%') 使用CONCAT()函数,(推荐✨) 4 使用bind标签(不推荐)

### MyBatis入门教程 #### 1. MyBatis简介 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。消除了几乎所有的 JDBC 代码和参数的手动设置以及结果集的检索。MyBatis 可以通过简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects) 映射成数据库中的记录[^1]。 #### 2. 日志输出配置 为了方便调试SQL语句,在`mybatis-config.xml`文件中可以加入如下配置: ```xml <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> ``` 这会使得执行的所有SQL语句都会被打印到控制台,便于开发人员查看和排查问题。 #### 3. 参数传递方式对比 在MyBatis中提供了两种不同的占位符用于构建动态SQL:`#{}` 和 `${}` 。其中 `#{}` 表达式的值会被视为预编译语句的一个参数,并由JDBC驱动自动处理转义字符;而`${}` 则直接替换为变量的实际内容,存在SQL注入风险,因此建议优先使用`#{}` 来防止潜在的安全隐患。 #### 4. 实体类定义 对于每一个要操作的数据表来说都需要创建对应的Java Bean对象即实体类。这里展示了一个基于 Lombok 注解简化后的User实体类例子: ```java import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @Data @TableName("user") public class User { @TableId(value = "id", type = IdType.AUTO) private Long id; private String name; private Integer age; private String email; } ``` 此段代码利用了Lombok库提供的`@Data`自动生成getter/setter方法,减少了样板代码的数量并提高了可读性和维护效率[^2]。 #### 5. Mapper 接口编写 Mapper接口是用来声明针对特定数据表的操作行为的地方。通常情况下只需要按照一定命名规则定义好相应的方法签名即可完成CRUD功能实现。例如下面是一个典型的UserMapper接口片段: ```java package com.example.mapper; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface UserMapper { int insert(User user); List<User> selectAll(); void updateById(Long id, User updatedUser); boolean deleteById(Long id); } ``` 上述示例展示了如何简单地定义一些基础性的增删改查函数原型,实际业务逻辑则交给了XML映射文件或者内联注解去具体描述。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值