初识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语句并执行。

java 复制代码
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文件内容

java 复制代码
?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> 元素来指定映射文件或映射器接口的位置。

java 复制代码
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的获取是固定的写法,在下面的增删改查操作中会很频繁的使用,所以我们将其创建过程的代码封装成一个工具类

java 复制代码
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后将如下内容放入

java 复制代码
<?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">
java 复制代码
 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

java 复制代码
<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

java 复制代码
<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文件中引用确切的类时,则可以简写。下图对应如上我们设置的别名

html 复制代码
核心配置文件中的标签必须按照固定的顺序:
    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、查询一个实体类对象

java 复制代码
在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、查询单个数据

java 复制代码
在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集合

java 复制代码
在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集合中获取

java 复制代码
在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集合

java 复制代码
在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、模糊查询

java 复制代码
在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、批量删除

java 复制代码
在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、动态设置表名

java 复制代码
在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、添加功能获取自增的主键

java 复制代码
在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

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

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

java 复制代码
在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标签,为字段和属性建立映射关系

html 复制代码
<!--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。

方法一:

级联方式处理映射关系

java 复制代码
在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处理映射关系

html 复制代码
<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,

java 复制代码
在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查询部分中:

java 复制代码
在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以及查询结果中的某个字段设置为分步查询的条件)

延迟加载:

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

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

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

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

没有设置则是

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

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

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

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

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

3、一对多映射处理

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

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

方法一:

用collection处理映射关系

java 复制代码
在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部分查询中

java 复制代码
在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查询部分中

java 复制代码
在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中属性名

和字段可以一一对应)

最后得到结果

相关推荐
loveLifeLoveCoding1 分钟前
Java List sort() 排序
java·开发语言
草履虫·7 分钟前
【Java集合】LinkedList
java
AngeliaXue9 分钟前
Java集合(List篇)
java·开发语言·list·集合
世俗ˊ10 分钟前
Java中ArrayList和LinkedList的比较
java·开发语言
zhouyiddd15 分钟前
Maven Helper 插件
java·maven·intellij idea
攸攸太上23 分钟前
Docker学习
java·网络·学习·docker·容器
Milo_K31 分钟前
项目文件配置
java·开发语言
程序员大金35 分钟前
基于SpringBoot+Vue+MySQL的养老院管理系统
java·vue.js·spring boot·vscode·后端·mysql·vim
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS网上购物商城(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
nsa652231 小时前
Knife4j 一款基于Swagger的开源文档管理工具
java