初识MyBatis(一)基于配置文件下的一些增删改查

 MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录

 MyBatis 是一个 半自动的ORM(Object Relation Mapping)框架

创建好maven工程(设置打包方式为jar,引入依赖)

创建MyBatis的核心配置文件(主要用于配置连接数据库的环境以及MyBatis的全局配置信息,存放的位置是src/main/resources目录下

习惯上命名为mybatis-config.xml(将来整合Spring 之后,这个配置文件可以省略

如上在该配置文件的内容

(其中如下路径方式是以main\resources目录为准,如果UserMapper.xml在resources目录下的话,则直接写其名字即可。下面可以看出我的文件路径路径是main\resources\mappers\UserMapper.xml)

<mapper resource="mappers\UserMapper.xml"/>

创建mapper接口:MyBatis中的mapper接口相当于以前的dao。但是区别在于,mapper仅仅是接口,我们不需要提供实现类,只需要创建接口,通过mybatis的一些功能来创建一些代理实现类,当我们调用接口中的方法,直接对应其中的sql语句并执行。

public interface UserMapper {
    int insertUser();
    void updateUser();
    int deleteUser();
    User getUserById();
    List<User> getAllUser();
}

(上图的方法,都是下面增删改使用到的方法)

创建完接口后,创建mybatis的映射文件

1、映射文件的命名规则: 表所对应的实体类的类名+Mapper.xml 例如:表t_user,映射的实体类为User,所对应的映射文件为UserMapper.xml
(下为UserMapper.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">
       <!-- 一个映射文件对应一个实体类,对应一张表的操作
        MyBatis映射文件用于编写SQL,访问以及操作表中的数据-->
<mapper namespace="personal.august.mybatis.mapper.UserMapper">
   <!-- MyBatis中可以面向接口操作数据,要保证两个一致:
    a>mapper接口的全类名和映射文件的命名空间(namespace)保持一致
    b>mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致-->
    <!--int insertUser(); sql语句对应的方法-->
    <insert id="insertUser">
        insert into t_user values(1,'wang','12345',22,'男','1@q.com')
    </insert>
</mapper>

MyBatis映射文件存放的位置是src/main/resources/mappers目录下

(无论映射文件的存放位置如何,都需要在MyBatis的配置文件中正确地指定映射文件的位置。在MyBatis的配置文件中可以使用 <mappers> 元素来指定映射文件或映射器接口的位置。

public class MybatisTest {
   @Test
   public void testInsert() throws IOException {
      //读取MyBatis的核心配置文件
      InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-comfig.xml");
      //创建SqlSessionFactoryBuilder对象
      SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
      //通过核心配置文件所对应的字节输入流创建工厂类SqlSessionFactory,生产SqlSession对象
      SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);
      //创建SqlSession对象,此时通过SqlSession对象所操作的sql都必须手动提交或回滚事务
      //SqlSession sqlSession = sqlSessionFactory.openSession();
      //创建SqlSession对象,此时通过SqlSession对象所操作的sql都会自动提交
      SqlSession sqlSession = sqlSessionFactory.openSession(true);
      //通过代理模式创建UserMapper接口的代理实现类对象
      UserMapper mapper = sqlSession.getMapper(UserMapper.class);
      //调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配元素文件,通过调用的方法名匹配
      //映射文件中的SQL标签,并执行标签中的SQL语句
      int result = mapper.insertUser();
     
      //提供sql以及的唯一标识找到sql并执行,唯一标识是namespace.sqlId(记得用双引号引起)
//      int result = sqlSession.insert("personal.august.mybatis.mapper.UserMapper.insertUser");
      System.out.println(result);
      sqlSession.close();
   }
}

(补充:

SqlSession:代表Java程序和数据库之间的会话。(HttpSession是Java程序和浏览器之间的 会话)

SqlSessionFactory:是“生产”SqlSession的“工厂”。

工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的 相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象。

navicat中在表名那里右键点击设计表则可以设置表结构

因为sqlSession的获取是固定的写法,在下面的增删改查操作中会很频繁的使用,所以我们将其创建过程的代码封装成一个工具类

public class SqlSessionUtil {
    public static SqlSession getSqlSession()  {
        SqlSession sqlSession = null;
        try {
            //获取核心配置文件的输入流
            InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-comfig.xml");
            //获取SqlSessionFactoryBuilder
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            //获取SqlSessionFactory
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);
            //获取SqlSession对象
            sqlSession = sqlSessionFactory.openSession(true);
        } catch (IOException e) {
           e.printStackTrace();
        }
        return sqlSession;
    }
}

加入log4j日志功能

 在pom.xml文件中加入相关依赖

加入log4j的配置文件

log4j的配置文件名为log4j.xml,存放的位置是src/main/resources目录下

创建文件发现点小问题,发现后缀为tld,

可以按照如图去创建xml文件的模板。下次新建则可以使用自己创建的模板

新建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>

(补充:日志级别

FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试) 从左到右打印的内容越来越详细

 Mybatis的增删改查

update更新修改:将wang改为today,密码改为123

在UserMapper中声明方法updateUser()

在UserMapper.xml文件中的对应的namespace下增加sql语句(下图对应id=updateUser(方法名)

<mapper namespace="personal.august.mybatis.mapper.UserMapper">

 public void testUpdate(){
      SqlSession sqlSession = SqlSessionUtil.getSqlSession();
      UserMapper mapper = sqlSession.getMapper(UserMapper.class);
      mapper.updateUser();
      sqlSession.close();
}

调用自己封装的工具类SqlSessionUtil创建SqlSession,生成代理类对象mapper,调用updateUser方法。得到如下预期中的修改结果

delete删除:删除id=1的记录

得到如下图的预期结果

select查询:

如果查询结果为一条数据则可以转换为实体类对象,如果是多条数据则转换为实体类对象的集合

查询一个实体类对象

按照之前的增删改操作去实现查询出现如上报错(我们未获得对应的结果集

如下修改(在select的标签下设置resultType属性,如果是多个字段要用resultMap属性

(resultType的类要有唯一路径说明

修改后得到如下正确结果

查询集合(查询所有数据)

(这里resultType的值和上面一样是一个类而不是最后呈现的类型list,因为是先把数据先转换为实体类对象后再放入集合)

得到如下查询结果

(注意:

1、查询的标签select必须设置属性resultType或resultMap,用于设置实体类和数据库表的映射关系

resultType:自动映射,用于属性名和表中字段名一致的情况

resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况(下面内容会涉及)

2、当查询的数据为多条时,不能使用实体类作为返回值,只能使用集合,否则会抛出异常 TooManyResultsException;但是若查询的数据只有一条,可以使用实体类或集合作为返回值

核心配置文件(以我取名示例mybatis-config.xml)

可以创建properties文件方式来写配置文件

 (在文件中的内容,不要故意空格,如上加了jdbc的后缀,为了标识和易于理解,如果出现多个相同内容,也可以很好的区分,This is a good habit)

用文件方式来完善内容,则要引入到配置文件中,用标签properties

<configuration>
    <!--引入properties文件,此时就可以${属性名}的方式访问属性值-->
    <properties resource="mappers/jdbc.properties"></properties>
 <environments default="development">
        <environment id="development" >
             <transactionManager type="JDBC"/>
               <dataSource type="POOLED">
                <!--设置驱动类的全类名-->
                <property name="driver" value="${jdbc.driver}"/>
                <!--设置连接数据库的连接地址-->
                <property name="url" value="${jdbc.url}"/>
                <!--设置连接数据库的用户名-->
                <property name="username" value="${jdbc.username}"/>
                <!--设置连接数据库的密码-->
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入映射文件-->
    <mappers>
        <mapper resource="mappers\UserMapper.xml"/>
    </mappers>
</configuration>

(<properties resource="mappers/jdbc.properties"></properties>,如果文件在resources目录下,则直接写文件名即可)

在配置文件中的其他标签

typeAliases

<typeAliases>
        <!--
            typeAlias:设置某个具体的类型的别名
            属性:
            type:需要设置别名的类型的全类名
            alias:设置此类型的别名,若不设置此属性,该类型拥有默认的别名,即类名且不区分大小
            写
            若设置此属性,此时该类型的别名只能使用alias所设置的值
            -->
        <typeAlias type="personal.august.mybatis.pojo.User"></typeAlias>
        <!--<typeAlias type="personal.august.mybatis.pojo.User" alias="abc">
        </typeAlias>-->
        <!--以包为单位,设置改包下所有的类型都拥有默认的别名,即类名且不区分大小写-->
<!--        <package name="personal.august.mybatis.pojo"/>-->
    </typeAliases>

(<typeAlias type="personal.august.mybatis.pojo.User"></typeAlias>,未指定别名,则默认为类名User,且不区分大小写)

设置别名后,我们在UserMapper.xml文件中引用确切的类时,则可以简写。下图对应如上我们设置的别名

核心配置文件中的标签必须按照固定的顺序:
    properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,
reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?

<mappers> 在mybatis-config.xml文件中映射文件的引用

<mappers>
    <mapper resource="mappers\UserMapper.xml"/>
</mappers>

在此之前我们是这样引入的,但是,按照这样的方式会比较麻烦,因为一个类对应一个配置文件,这样要写很多如果后面需要的话。

则我们可以通过包来引入配置文件,注意:

映射文件所在的包和mapper接口所在的包一样

只有用/分开去建文件才可以,因为我们点的是新建目录。personal/august/mybatis这样写才可以得到和接口所在一样的包。如果是用personal.august.mybatis则如下图

可以发现在idea中看不出,但实际上是一整个文件夹名,而不是和接口所在包相同。

如上图,根据我们UserMapper接口所在的包,我们为配置文件也创建相同的包,并放入。

以包的方式引入映射文件,必须满足两个条件
1、mapper接口和映射文件所在的包必须一致
2、mapper接口和映射文件的名字必须一致

在满足上述条件后,我们则可以这样引入配置文件(直接写配置文件所在包名即可,且后面还有在该包下的配置文件,也可不必修改。

设置同样的包是为了文件可以在加载完成后放在一起

MyBatis获取参数值的两种方式:${}和#{}
${}的本质就是字符串拼接,#{}的本质就是占位符赋值
${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;但是#{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号。

我们要做的就是在映射文件中获取sql语句对应方法的参数,然后拼接到sql语句中

1、单个字面量类型的参数
若mapper接口中的方法参数为单个的字面量类型
此时可以使用${}和#{}以任意的名称获取参数的值,注意${}需要手动加单引号

在UserMapper接口中创建该查询方法:
User getUserByUsername(String username);
创建测试类(同名包下)ParameterTest创建测试方法:
public void testGetUserByUsername(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User today = mapper.getUserByUsername("today");
    System.out.println(today);
}

(如上图是使用#{},且查询到的是一条数据的情况,下面会讲到多条数据的处理,

select * from t_user where username=#{username};#里的username可以是任意名字,因为我们获取的参数值,不会识别这个名字,所以即使是abc也可,建议取的有意义一些(但是不同版本mybatis可能不同

如果是使用${}则要加‘’,如下

select * from t_user where username='${username}';


2、多个字面量类型的参数
 

按照第一种情况(只有一个字面量去操作时出现如下报错

在UserMapper接口中创建该查询方法:
User checklogin(String username, String password);
创建测试方法:
public void testcheckelogin(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.checklogin("haha", "111");
    System.out.println(user);
}

若mapper接口中的方法参数为多个时

此时MyBatis会自动将这些参数放在一个map集合中,

以arg0,arg1...为键,以参数为值;以param1,param2...为键,以参数为值;

因此只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号

可知如下是可以的

select * from t_user where username = #{arg0} and password = #{arg1};
select * from t_user where username = #{param1} and password = #{param2};

上图知,效果等价于:
select * from t_user where username = #{arg0} and password = #{arg0};
而如果想混搭arg和param的正确写法为
select * from t_user where username = #{arg0} and password = #{param2};
select * from t_user where username = #{param1} and password = #{arg1};
(注意param是从1开始,arg是0)

如果是${},则记得要加‘’,如下

select * from t_user where username = '${arg0}' and password = '${arg1}';

3、map集合类型的参数
若mapper接口中的方法需要的参数为多个时

此时可以手动创建map集合,将这些数据放在map中。只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号

在UserMapper接口中
User checkLoginByMap(Map<String, Object> map);
创建测试方法:
public void testcheckeLoginByMap(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Object> map = new HashMap<>();
    map.put("username", "haha");
    map.put("password","111");
    User user = mapper.checkLoginByMap(map);
    System.out.println(user);
}

上图可知我们得要使用我们自己设置得参数名才可以,

如果是${},则记得要加‘ ’,如下

select * from t_user where username = '${username}' and password = '${password}';

4、实体类类型的参数
若mapper接口中的方法参数为实体类对象时
此时可以使用${}和#{},通过访问实体类对象中的属性名获取属性值,注意${}需要手动加单引号

在UserMapper接口中
void insertUser(User user);
创建测试方法:
public void testinsertUser(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user= new User(1, "saturday", "1234567", 22, "女", "99@qq.com");
    mapper.insertUser(user);
}
在UserMapper.xml中
<insert id="insertUser" >
    insert t_user values(null,#{username},#{password},#{age},#{gender},#{email});
</insert>

5、使用@Param标识参数
可以通过@Param注解标识mapper接口中的方法参数
此时,会将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;以
param1,param2...为键,以参数为值;只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号

在UserMapper接口中
void checkLoginByParam(@Param("username")String username,@Param("password")String password);
创建测试方法:
public void testcheckLoginByParam(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.checklogin("what", "234");
    System.out.println(user);
}

在这种方法中,我们有两种访问方式(username,password)(param1,param2)

所以(username,param2)(param1,password)也是可以的

select * from t_user where username = #{username} and password = #{password};
select * from t_user where username = #{param1} and password = #{param2};

 MyBatis的各种查询功能

1、查询一个实体类对象

在SelectMapper接口中
User getUserById(@Param("id")Integer id);
List<User> getAllUser();

创建测试类(同名包下)SelectMapperTest 创建测试方法:
public void testGetUserById(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
    User user = mapper.getUserById(1);
    System.out.println(user);
}

public void testGetALLUser(){
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
    List<User> allUser = mapper.getAllUser();
    allUser.forEach(System.out::println);
}

在SelectMapper.xml中
 <!--User getUserById(@Param("id")Integer id);-->
    <select id="getUserById" resultType="User">
        select * from t_user where id = #{id};
    </select>

    <!--List<User> getAllUser();-->
    <select id="getAllUser" resultType="User">
        select * from t_user;
    </select>

这个其实就是上面出现过的查询,要注意的是,当我们查询的是多条数据时,返回类型不能是一个实体类。

2、查询单个数据

在SelectMapper接口中
Integer getCount();

创建测试方法:
public void testGetCount(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
        Integer count = mapper.getCount();
        System.out.println(count);
    }

在SelectMapper.xml中
 <select id="getCount" resultType="java.lang.Integer">
        select count(*) from t_user;
    </select>
select count(*) from t_user;
其中的*可以是任意的数值,但是如果是字段则要注意,当该字段下的值为null时,不计数, <select id="getCount" resultType="java.lang.Integer">
其中 resultType的值可以是int,integer,大小写不区分
因为在MyBatis中,对于Java中常用的类型都设置了类型别名
* 例如:java.lang.Integer-->int|integer
* 例如:int-->_int|_integer
* 例如:Map-->map,List-->list

(补充,在看到用integer当方法返回值类型,而不是int时,有点疑惑,于是

包装类型和基本类型的差别(由方法返回值想到的问题)_boolean传参传基本类型还是包装类型_早点起床晒太阳的博客-CSDN博客

3、查询一条数据为map集合

在SelectMapper接口中
Map<String,Object> getUserByIdMap(@Param("id")Integer id);

创建测试方法:
public void testgetUserByIdMap(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
        Map<String, Object> userByIdMap = mapper.getUserByIdMap(2);
        System.out.println(userByIdMap);
    }

在SelectMapper.xml中
 <select id="getUserByIdMap" resultType="map">
        select * from t_user where id = #{id};
    </select>

我们要想清楚查询的返回值类型到底是什么,如果是实体类,则其内容就是按照实体类属性呈现,而如果是map集合,不是对应固定的属性输出,有什么内容就输出什么,例如

我创建了最后一条数据,用map集合查询,输出情况如下

4、查询多条数据为map集合(每个数据都是map集合

方式一:

将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,此时可以将这些map放在一个list集合中获取

在SelectMapper接口中
 List<Map<String,Object>> getALlUserToMap();

创建测试方法:
public void testgetALlUserToMap(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
        List<Map<String, Object>> aLlUserToMap = mapper.getALlUserToMap();
        System.out.println(aLlUserToMap);
    }

在SelectMapper.xml中
 <select id="getALlUserToMap" resultType="map">
        select * from t_user;
    </select>

方式二:

将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,并且最终要以一个map的方式返回数据,此时需要通过@MapKey注解设置map集合的键,值是每条数据所对应的map集合

在SelectMapper接口中
  @MapKey("id")
    Map getALlUserToMap1();

创建测试方法:
public void testgetALlUserToMap(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
        Map aLlUserToMap1 = mapper.getALlUserToMap1();
        System.out.println(aLlUserToMap1);
    }

在SelectMapper.xml中
<select id="getALlUserToMap1" resultType="map">
        select * from t_user;
    </select>

@MapKey("id"),即是设置以id为键,来输出查询到的多条map数据,以键值对的形式输出,
又因为map的键必须唯一,所以要用主键或唯一索引

 特殊SQL的执行

1、模糊查询

在SpecialSQLMapper接口中
List<User> getUserByLike(@Param("mohu") String mohu);

创建测试类(同名包下)SpecialSQLMapperTest 创建测试方法:
public void testgetUserByLike(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        SpecialSQLMapper mapper = sqlSession.getMapper(SpecialSQLMapper.class);
        List<User> t = mapper.getUserByLike("t");
        System.out.println(t);
    }

在SpecialSQLMapper.xml中
 <select id="getUserByLike" resultType="User">
        select * from t_user where username like '%${mohu}%';
    </select>

#{}相当于占位符,如上图被解析成问号

方法一:

用${}

select * from t_user where username like '%${mohu}%';
select * from t_user where username like concat('%','${mohu}','%');

在这里使用List集合是因为如果查询多条实体类数据才不会出错

方法二,三:

select * from t_user where username like concat('%',#{mohu},'%');
select * from t_user where username like "%"#{mohu}"%";

2、批量删除

在SpecialSQLMapper接口中
void deleteMoreUser(@Param("id")String ids);

创建测试方法:
 public void testdeleteMoreUser(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        SpecialSQLMapper mapper = sqlSession.getMapper(SpecialSQLMapper.class);
        mapper.deleteMoreUser("14,15");
    }

error:在SpecialSQLMapper.xml中
 <delete id="deleteMoreUser">
        delete from t_user where id in(#{id});
    </delete>

如果是如上用#{id},则最后会被解析成delete from t_user where id in(’14,15‘);加上了单引号。会报错

可以用${},

在SpecialSQLMapper.xml中
<delete id="deleteMoreUser">
    delete from t_user where id in(${id});
</delete>

除了这种方法,后面学的动态SQL还可以用它的foreach标签实现如下,达到等同效果

delete from t_user where id = 14 or id =15;

3、动态设置表名

在SpecialSQLMapper接口中
List<User> getUserList(@Param("tableName")String tablename);

创建测试方法:
 public void testGetUserList(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        SpecialSQLMapper mapper = sqlSession.getMapper(SpecialSQLMapper.class);
        List<User> t_user = mapper.getUserList("t_user");
        t_user.forEach(System.out::println);
    }

在SpecialSQLMapper.xml中
<select id="getUserList" resultType="User">
        select * from ${tableName};
    </select>

记得用了注解后,名字一般都是使用注解取的名为键,如上报错显示param1也可(报错因为我的注解名叫tableName,而我写成select * from ${tablename};

4、添加功能获取自增的主键

在SpecialSQLMapper接口中
void insertUser(User user);

创建测试方法:
 public void testinsertUser(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        SpecialSQLMapper mapper = sqlSession.getMapper(SpecialSQLMapper.class);
        User user = new User(0, "i'm not happy", "1889", 29, "男", "00@00");
        mapper.insertUser(user);
    }

在SpecialSQLMapper.xml中
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
        insert into t_user values (null,#{username},#{password},#{age},#{gender},#{email});
    </insert>
useGeneratedKeys:设置使用自增的主键
keyProperty:因为增删改有统一的返回值是受影响的行数,
因此只能将获取的自增的主键放在传输的参数user对象的某个属性中

在navicat中创表一开始没有设置自增和自增值为1,发现到后面改不了,而且如下图,无法实现输入null,实现自增功能而不报错

但是还是没找到设置方式,即使重新建表也没有解决。(想要达到那种id输入null,在数据表中插入的数据可以自增。

发现如果null不行,那就每次插入操作都填0就可,在数据表中可以实现自增,且值不为空。

 自定义映射resultMap

1、resultMap处理字段和属性的映射关系
若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射

首先我们有如下表t_emp,t_dept

两表对应的实体类中属性名如下

按照我们之前的操作直接查询

在EmpMapper接口中
Emp getEmpByEmpId(@Param("empId") Integer empId);

创建测试类(同名包下)EmpMapperTest 创建测试方法:
public void testgetEmpByEmpId(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp empByEmpId = mapper.getEmpByEmpId(3);
        System.out.println(empByEmpId);
    }

在SpecialSQLMapper.xml中
<select id="getEmpByEmpId" resultType="Emp">
        select * from t_emp where emp_id=#{empId};
    </select>

可以发现并没有完整查询出id为3的那行记录。但是age、gender可以,因为字段名和属性名相同,而其他不同,没有映射关系。(所以我们需要通过如下方法处理映射关系)

方法一:

如下解决,通过取别名的方式,对应属性名

<select id="getEmpByEmpId" resultType="Emp">
        select emp_id empId,emp_name empName,age, gender from t_emp where emp_id=#{empId};
    </select>

(注意注解用<!--  -->,刚这个select标签下用#注解一条信息,在运行时出错。

方法二:

可以在MyBatis的核心配置文件中设置一个全局配置信息mapUnderscoreToCamelCase,可
以在查询表中数据时,自动将_类型的字段名转换为驼峰
例如:字段名user_name,设置了mapUnderscoreToCamelCase,此时字段名就会转换为
userName

在mybatis-config.xml文件中,注意标签放置顺序

在EmpMapper.xml文件中:
<select id="getEmpByEmpId" resultType="Emp">
    select * from t_emp where emp_id=#{empId};
</select>

就可以顺利查询,但是这只是实现了取名用_取名的相似特点来转换名字,还是有点缺陷,需要和字段很好的对应。

方法三:

使用resultMap自定义映射处理

在EmpMapper.xml文件中:利用resultMap标签,为字段和属性建立映射关系

<!--resultMap:设置自定义映射
                    属性:
                    id:表示自定义映射的唯一标识
                    type:查询的数据要映射的实体类的类型
                    子标签:
                    id:设置主键的映射关系
                    result:设置普通字段的映射关系
                    association:设置多对一的映射关系
                    collection:设置一对多的映射关系
                    属性:
                    property:设置映射关系中实体类中的属性名
                    column:设置映射关系中表中的字段名
               -->
   <resultMap id="empResultMap" type="Emp">
       <id column="emp_id" property="empId"></id>
       <result column="emp_name" property="empName"></result>
   </resultMap>

    <!--Emp getEmpByEmpId(@Param("empId") Integer empId);-->
    <select id="getEmpByEmpId" resultMap="empResultMap">
        select * from t_emp where emp_id=#{empId};
    </select>

主要是要注意一些属性的用法

2、多对一映射处理

比如:查询员工信息以及员工所对应的部门信息

如下图,我们可以发现,按照之前的操作,我们得出dept=null(在emp实体类中添加了Dept dept这个成员变量)

因为我们查询的是dept_id和dept_name这两个属性,其映射的是deptId和deptName这两个属性,但是上述操作直接映射到Dept这个类型。但是我们查询到的没有可以和实体类型Dept对应的,所以该值为null。

方法一:

级联方式处理映射关系

在EmpMapper接口中
Emp getEmpAndDeptByEmpId(@Param("empId") Integer id);

创建测试方法:
public void testgetEmpAndDeptByEmpId(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp empByEmpId = mapper.getEmpAndDeptByEmpId(2);
        System.out.println(empByEmpId);
    }

在SpecialSQLMapper.xml中
<resultMap id="empAndDeptResultMap" type="Emp">
       <id column="emp_id" property="empId"></id>
       <result column="emp_name" property="empName"></result>
       <result column="age" property="age"></result>
       <result column="gender" property="gender"></result>
       <result column="dept_id" property="dept.deptId"></result>
       <result column="dept_name" property="dept.deptName"></result>
   </resultMap>

    <!--Emp getEmpAndDeptByEmpId(@Param("empId") Integer id);-->
    <select id="getEmpAndDeptByEmpId" resultMap="empAndDeptResultMap">
        select t_emp.*,t_dept.*
        from t_emp
        left join t_dept
        on t_emp.dept_id = t_dept.dept_id
        where emp_id=#{empId};
    </select>

(试了一下不把所有属性列出来其实也可以,因为本来名字就一样,在操作时发现可以查询到age,gender。)

方法二:

使用association处理映射关系

<resultMap id="empAndDeptResultMap" type="Emp">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
        <!--
            association:处理一对多的映射关系(处理实体类类型的属性)
            property:设置需要处理映射关系的属性的属性名
            javaType:设置要处理的属性的类型             -->
        <association property="dept" javaType="Dept">
            <id column="dept_id" property="deptId"></id>
            <result column="dept_name" property="deptName"></result>
        </association>
    </resultMap>

方法三:

分步查询

创建一个DeptMapper接口,映射文件DeptMapper.xml,

在DeptMapper接口中
Dept getEmpAndDeptByStepTwo(@Param("deptId") Integer deptId);

创建测试方法:
public void testgetEmpAndDeptByStep(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp empByEmpId = mapper.getEmpAndDeptByStepOne(2);
        System.out.println(empByEmpId);
    }

在DeptMapper.xml中
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
        select * from t_dept where dept_id=#{deptId};
    </select>

(需要注意的是在Dept查询部分,我们使用的是设置驼峰来实现字段和属性的映射,可能上面的信息没有体现出来)

在Emp查询部分中:

在EmpMapper接口中
Emp getEmpAndDeptByStepOne(@Param("empId") Integer id);


在EmpMapper.xml中
<resultMap id="empAndDeptByStepResultMap" type="Emp">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
        <association property="dept"
                        select="personal.september.resultmap.mapper.DeptMapper.getEmpAndDeptByStepTwo"
                        column="dept_id"></association>
    </resultMap>
    <!--Emp getEmpAndDeptByStepOne(@Param("empId") Integer id);-->
    <select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap">
        select * from t_emp where emp_id=#{empId};
    </select>

Emp查询部分为第一步查询,在association中的select填写的是sql的唯一标识(namespace.sqlid),其实就是当前接口的全类名.方法名。(设置分步查询,查询某个属性的值的sql的标识)

column填写的是查询条件,由下一步作为查询条件的字段,从第一个sql语句查询出来的结果中的某个字段作为下一个查询条件。而这里需要作为条件查询的就是dept_id。(将sql以及查询结果中的某个字段设置为分步查询的条件)

延迟加载:

分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息:

lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载

此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType="lazy(延迟加载)|eager(立即加载)"

当我们只是查询员工的名字时,设置了延迟加载出现的结果如下:

没有设置则是

如上可以看出,设置了延迟加载只会运行我们需要查询的,而没有设置则是全部代码都运行了(第一步可以得到我们想要的结果,没有延迟加载时,两步都执行了)

在开启了延迟加载的环境中,通过该属性设置当前分布查询是否使用延迟加载

fetchType="lazy(延迟加载)|eager(立即加载)"

所以可以在settings标签中加上如下最后两条内容即可(它们两个默认值为false

如果按需加载为true,则不管要查询什么,即使是开启了延迟加载,都会将所有代码都执行。所以延迟加载的设置即使只要lazyLoadingEnabled就可以开启,但是也和aggressiveLazyLoading有关。

3、一对多映射处理

比如:根据部门id查新部门以及部门中的员工信息

(按照对一,对应对象,对多对应集合的说法,所以一对多是对应集合,我们要在Dept实体类中新增一个集合,

方法一:

用collection处理映射关系

在DeptMapper接口中
Dept getDeptAndEmptByDeptId(@Param("deptId") Integer deptId);


在DeptMapper.xml中
<resultMap id="deptAndEmpResultMap" type="Dept">
        <id column="dept_id" property="deptId"></id>
        <result column="dept_name" property="deptName"></result>
        <!--
            ofType:设置collection标签所处理的集合属性中存储数据的类型
            -->
        <collection property="emps" ofType="Emp">
            <id column="emp_id" property="empId"></id>
            <result column="emp_name" property="empName"></result>
            <result column="age" property="age"></result>
            <result column="gender" property="gender"></result>
        </collection>
    </resultMap>
    <!--Dept getDeptAndEmptByDeptId(@Param("deptId") Integer deptId);-->
    <select id="getDeptAndEmptByDeptId" resultMap="deptAndEmpResultMap">
        select * from t_dept
        left join t_emp
        on t_dept.dept_id =t_emp.dept_id
        where t_dept.dept_id=#{deptId};
    </select>

创建测试方法:
public void testgetDeptAndEmptByDeptId(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        Dept deptAndEmptByDeptId = mapper.getDeptAndEmptByDeptId(1);
        System.out.println(deptAndEmptByDeptId);
    }

方法二:

分步查询

第一步查询部门信息

在Dept部分查询中

在DeptMapper接口中
Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId);


在DeptMapper.xml中
<resultMap id="deptAndEmpByStep" type="Dept">
        <id column="dept_id" property="deptId"></id>
        <result column="dept_name" property="deptName"></result>
       <collection property="emps" 
                      select="personal.september.resultmap.mapper.EmpMapper.getDeptAndEmpByStepTwo"
                      column="dept_id"></collection>
    </resultMap>
    <!--Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId);-->
    <select id="getDeptAndEmpByStepOne" resultMap="deptAndEmpByStep">
        select * from t_dept where dept_id=#{deptId};
    </select>

创建测试方法:
public void testgetDeptAndEmpByStepOne(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        Dept deptAndEmpByStepOne = mapper.getDeptAndEmpByStepOne(1);
        System.out.println(deptAndEmpByStepOne);
    }

第二步,根据部门id查询部门中的所有员工

在Emp查询部分中

在EmpMapper接口中
List<Emp> getDeptAndEmpByStepTwo(@Param("deptId") Integer id);


在EmpMapper.xml中
<select id="getDeptAndEmpByStepTwo" resultType="Emp">
        select * from t_emp where dept_id=#{deptId};
    </select>

(注意:

在接口中的这个方法的返回值是List集合,我们选择的类型要和Dept实体类中创造的List<Emp>  emps类型相对应。因为我们是把在collection标签的select属性中查询出的结果赋值给emps,所以类型是要相同的。

还有在mybatis-config.xml文件中我们已经设置了将下划线映射为驼峰,所以emp中属性名

和字段可以一一对应)

最后得到结果

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忘记578

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值