Mybatis学习

发布时间 2023-03-22 21:11:44作者: 奚罗罗

Mybatis

第一个mybatis程序

1、搭建环境

搭建数据库——>新建项目,普通maven项目——>删除src目录(作为父工程)——>导入maven依赖

<!--    导入依赖-->
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!-- mybatis驱动 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!-- junit测试包 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!-- log4j日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>

2、创建子模块

  • 编写mybatis核心配置文件,注意注册mapper

    <?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 核心配置文件-->
    <configuration>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=true&amp;serverTimezone=UTC"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
    </configuration>
    

    注册mapper

    <!--    每一个mapper.xml都需要在这个mybatis核心文件中注册-->
        <mappers>
            <mapper resource="com/luo/dao/UserMapper.xml"></mapper>
        </mappers>
    
  • 编写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;

//sqlsessionFactory构建sqlsession
public class MyBatisUtils {
    private static SqlSessionFactory factory=null;
    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream in= Resources.getResourceAsStream(resource);
            //创建SqlSessionFactory对象
            factory=new SqlSessionFactoryBuilder().build(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //获取SqlSession的方法
    public static SqlSession getSqlSession() {
        SqlSession sqlSession=null;
        if(factory!=null) {
            sqlSession=factory.openSession();//非自动提交事务
        }
        return sqlSession;
    }
}

3、编写代码

  • 实体类

    package com.luo.pojo;
    
    public class User {
        private int id;
        private String name;
        private String pwd;
    
        public User() {
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getPwd() {
            return pwd;
        }
    
        public void setPwd(String pwd) {
            this.pwd = pwd;
        }
    
        public User(int id, String name, String pwd) {
            this.id = id;
            this.name = name;
            this.pwd = pwd;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", pwd='" + pwd + '\'' +
                    '}';
        }
    }
    
  • Dao接口

    package com.luo.dao;
    
    import com.luo.pojo.User;
    
    import java.util.List;
    
    public interface UserDao {
        List<User> getUserList();
    }
    
  • 接口实现类,由原来的UserDaoImpl转化为一个mapper配置文件

<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace绑定一个对应的Dao/mapper接口-->
<mapper namespace="com.luo.dao.UserDao">
<!--    select查询语句-->
    <select id="getUserList" resultType="com.luo.pojo.User">
        select *from user
    </select>
</mapper>

4、测试

  • junit测试

    package com.luo.dao;
    
    import com.luo.pojo.User;
    import com.luo.utils.MyBatisUtils;
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    import java.util.List;
    
    public class UserDaoTest {
        @Test
        public void test(){
            //获取sqlSession对象
            SqlSession sqlSession = MyBatisUtils.getSqlSession();
            //getMapper()
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            List<User> userList = userDao.getUserList();
            for (User user : userList) {
                System.out.println(user);
            }
            //关闭sqlSession
            sqlSession.close();
        }
    }
    

    注意:如果遇到资源无法被导出的问题,在pom.xml中配置resources

    <!--在build中配置resources,来防止我们资源导出失败的问题-->
        <build>
            <resources>
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                </resource>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>true</filtering>
                </resource>
            </resources>
        </build>
    

CRUD

1、在接口中写方法

public interface UserDao {
    List<User> getUserList();

    //根据id查询用户
    User getUserById(int id);

    //添加用户
    int addUser(User user);

    //修改用户
    int updateUser(User user);

    //删除用户
    int deleteUser(int id);
}

2、在xml文件中写标签

<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace绑定一个对应的Dao/mapper接口-->
<mapper namespace="com.luo.dao.UserDao">
<!--    select查询语句-->
    <select id="getUserList" resultType="com.luo.pojo.User">
        select *from user
    </select>

    <select id="getUserById" resultType="com.luo.pojo.User" parameterType="int">
        select *from user where id = #{id}
    </select>

    <select id="addUser" parameterType="com.luo.pojo.User">
        insert into user values (#{id},#{name},#{pwd})
    </select>

    <update id="updateUser" parameterType="com.luo.pojo.User">
        update user
        set id = #{id},name=#{name},pwd = #{pwd}
        where id=4;
    </update>

    <delete id="deleteUser" parameterType="int">
        delete
        from user
        where id = #{id};
    </delete>
</mapper>
  • namespace中的包名要和mapper/dao接口的包名一致

  • id:就是对应的namespace中的方法名

  • resultType:sql语句中的返回值

  • parameterType:参数类型

  • 注意:需要idea连接数据库,写sql语句才会有提示

3、写测试代码

public class UserDaoTest {
    @Test
    public void test(){
        //获取sqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        //getMapper()
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        List<User> userList = userDao.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }
        //关闭sqlSession
        sqlSession.close();
    }

    @Test
    public void getUserById(){
        //获取sqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        //getMapper()
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        User user = userDao.getUserById(1);
        System.out.println(user);
        //关闭sqlSession
        sqlSession.close();
    }

    @Test
    public void addUser(){
        //获取sqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        //getMapper()
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        userDao.addUser(new User(4,"赵4","123"));
        //增删改需要提交事务
        sqlSession.commit();
        //关闭sqlSession
        sqlSession.close();
    }

    @Test
    public void updateUser(){
        //获取sqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        //getMapper()
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        userDao.updateUser(new User(4,"赵四","123456"));
        //增删改需要提交事务
        sqlSession.commit();
        //关闭sqlSession
        sqlSession.close();
    }

    @Test
    public void deleteUser(){
        //获取sqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        //getMapper()
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        userDao.deleteUser(4);
        //增删改需要提交事务
        sqlSession.commit();
        //关闭sqlSession
        sqlSession.close();
    }
}

Map和模糊查询拓展

Map

假设我们的实体类或者数据库中的表,字段或参数过多,我们可以用map来传递参数;

//只修改密码,使用map传参
int updatePwd(Map<String,Object> map);
<update id="updatePwd" parameterType="Map">
    update user
    set pwd = #{pwd}
    where id = #{id};
</update>
@Test
public void updatePwd(){
    //获取sqlSession对象
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    //getMapper()
    UserDao userDao = sqlSession.getMapper(UserDao.class);
    Map<String, Object> map = new HashMap<String, Object>();
    //只想修改密码,就不用把每个字段都写上
    map.put("id",3);
    map.put("pwd",11111);
    userDao.updatePwd(map);
    //增删改需要提交事务
    sqlSession.commit();
    //关闭sqlSession
    sqlSession.close();
}

模糊查询拓展

java代码执行的时候,传递通配符%%;

//模糊查询
List<User> getUserLike(String name);
<select id="getUserLike" resultType="com.luo.pojo.User">
    select *from user where name like #{value}
</select>
@Test
public void getUserLike(){
    //获取sqlSession对象
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    //getMapper()
    UserDao userDao = sqlSession.getMapper(UserDao.class);
    //查询所有姓李的人
    List<User> userList = userDao.getUserLike("%李%");
    for (User user : userList) {
        System.out.println(user);
    }
    //关闭sqlSession
    sqlSession.close();
}

配置解析

核心配置文件:mybatis-config.xml

属性优化

我们可以通过properties属性来实现引用配置文件

  • 编写一个配置文件 db.properties

    driver = com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    username=root
    password=123456
    
  • 在核心配置文件里面引入

    <properties resource="db.properties"/>
    

别名优化(typeAliases)

  • 给实体类写别名

    <typeAliases>
        <typeAlias type="com.luo.pojo.User" alias="User"></typeAlias>
    </typeAliases>
    

​ 这样在需要使用com.luo.pojo.User时,可以用User代替

  • 给包别名

    <typeAliases>
        <package name="com.luo.pojo"/>
    </typeAliases>
    

​ 这样在需要使用这个包的实体类时间,默认别名为这个类的类名,首 字母小写。比如需要使用com.luo.pojo.User时,可以用user代替。也可以在实体类前通过注解指定别名@Alias。

映射器说明(mappers)

  • 方式一:使用相对于类路径的资源引用[推荐使用]

    <mappers>
        <mapper resource="com/luo/dao/UserMapper.xml"></mapper>
    </mappers>
    
  • 方式二:使用映射器接口实现类的完全限定类名

        <mappers>
            <mapper class="com.luo.dao.UserMapper"/>
        </mappers>
    

注意:接口和他的mapper配置文件必须同名和在同一个包下

  • 方式三:通过pakage

      <mappers>
            <package name="com.luo.dao"/>
        </mappers>
    

注意:接口和他的mapper配置文件必须同名和在同一个包下

解决属性名和字段名不一致的问题

如果实体类的属性名和数据库中的字段名不一致的话,查询出来可能会为空。

解决方法:

  • 改sql语句,起别名:pwd as “password”

  • resultMap(结果集映射):

    <resultMap id="UserMap" type="User">
       <result column="pwd" property="password"/> 
    </resultMap>
    

日志

如果一个数据库操作出了异常,日志可以帮助我们排错。在核心配置文件中配置,具体使用哪个日志。

标准日志,可以直接使用

<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

log4j

1、导包

<!-- log4j日志 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2、log4j.properties

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/luo.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

3、配置log4j为日志的实现

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

4、log4j的使用

分页

使用Limit分页

SELECT *from user limit startIndex,pageSize;
SELECT *from user limit 3;#[0,n]

使用mybatis实现分页

  • 接口
List<User> getUserLimit(Map<String,Integer> map);
  • mapper.xml
<select id="getUserLimit" resultType="User" parameterType="map">
   select *from user limit #{startIndex},#{pageSize}
</select>
  • 测试
@Test
public void getUserLimit(){
    //获取sqlSession对象
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    //getMapper()
    UserMapper userDao = sqlSession.getMapper(UserMapper.class);
    Map<String,Integer> map = new HashMap<String,Integer>();
    map.put("startIndex",0);
    map.put("pageSize",2);
    List<User> userList = userDao.getUserLimit(map);
    for (User user : userList) {
        System.out.println(user);
    }
    //关闭sqlSession
    sqlSession.close();
}

Lombok

1、安装插件

2、导包

<!-- lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
</dependency>

3、使用

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}

@Data:无参构造,get,set,tostring,hashcode,equals

@AllArgsConstructor:有参构造

@NoArgsConstructor:无参构造

复杂查询环境搭建

步骤

  • 新建表teacher,student
  • 写对应实体类Teacher,Student
  • 写实体类对应接口TeacherMapper,StudentMapper
  • 写对应xml文件
  • 测试运行

多对一处理:查询所有学生及对应的老师信息

方式一:按照查询嵌套处理(子查询)

    //查询所有学生及对应的老师信息
    List<Student> getStudentInfo();
<!--    思路:1、查询所有学生2、查询tid对应的老师-->
    <resultMap id="StudentInfo" type="student">
<!--        对象用association,javaType说明teacher是一个对象,再嵌套查询-->
        <association property="teacher" column="tid" javaType="teacher" select="getTeacher"></association>
    </resultMap>
    <select id="getStudentInfo" resultMap="StudentInfo">
        select *from student
    </select>
    <select id="getTeacher" resultType="teacher">
        ##{tid}里面这个tid可以换成任意值,mybatis会自动匹配
        select *from teacher where id = #{tid}
    </select>
@Test
public void test(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    StudentMapper Mapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> studentList = Mapper.getStudentInfo();
    for (Student student : studentList) {
        System.out.println(student);
    }
    sqlSession.close();
}

方式二:按照结果嵌套处理(联表查询)

       <select id="getStudentInfo" resultMap="StudentInfo">
           #注意:这里必须取别名
           select s.id sid,s.name sname,t.name tname
           from student s,teacher t
           where s.tid = t.id
       </select>
       <resultMap id="StudentInfo" type="student">
           <result property="id" column="sid"/>
           <result property="name" column="sname"></result>
<!--           使用association映射-->
           <association property="teacher" javaType="teacher">
               <result property="name" column="tname"/>
           </association>
       </resultMap>

一对多处理:查询指定老师及对应的学生信息

方式一:按照结果嵌套处理(联表查询)

//查询指定老师及其所有学生
Teacher getTeacher(int id);
<select id="getTeacher" resultMap="teacherInfo">
    select t.id tid,t.name tname,s.id sid,s.name sname
    from teacher t,student s
    where t.id = #{id} and t.id = s.tid
</select>
 <resultMap id="teacherInfo" type="teacher">
     <result property="id" column="tid"/>
     <result property="name" column="tname"/>
     <!--       集合中的泛型信息用ofType获取-->
     <collection property="studentList" ofType="student" >
         <result property="id" column="sid"/>
         <result property="name" column="sname"/>
     </collection>
 </resultMap>
@Test
public void test(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);
    System.out.println(teacherMapper.getTeacher(1));
    sqlSession.close();
}

方式二:按照查询嵌套处理(子查询)

  <select id="getTeacher" resultMap="teacherInfo">
     select *from teacher where id = #{id}
  </select>
   <resultMap id="teacherInfo" type="teacher">
<!--       这里要写column,javaType两个属性,方式一无需写-->
      <collection property="studentList" column="id" javaType="ArrayList" ofType="student" select="getStudentByTeacherId"/>
   </resultMap>
    <select id="getStudentByTeacherId" resultType="student">
        select *from student where tid = #{tid}
    </select>

小结

1、关联-association 多对一

2、集合-collection 一对多

3、javaType & ofType

​ javaType:用来指定实体类中属性的集合

​ ofType:用来指定映射到List或集合中的pojo类型

动态SQL

动态sql就是根据不同的条件生成不同的sql语句

搭建环境

与前面一致,可以添加一个生成随机id的工具类

//生成随机id的工具类
public class IDutils {
    public static String getId(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }

    @Test
    public void test(){
        System.out.println(IDutils.getId());
    }
}

IF语句

例子:有条件的查询

//查询
List<Blog> queryBlog(Map map);
 <select id="queryBlog" parameterType="map" resultType="blog">
        select *from blog 
        <where>
            #查特定title的blog
            <if test="title != null">
                title = #{title}
            </if >
            <if test="author != null">
                and author=#{author}
            </if>
        </where>
    </select>
public void test(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    map.put("title","Mybatis");
    List<Blog> blogs = mapper.queryBlog(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    sqlSession.close();
}

choose,when,otherwise(相当于switch)

例子:给了什么就按什么查询

<select id="queryBlogChoose" resultType="blog" parameterType="map">
    select *from blog
    <where>
        <choose>
            <when test="title != null">
                title = #{title}
            </when>
            <when test="views != null">
                and views = #{views}
            </when>
            <otherwise>
                and 1 = 1
            </otherwise>
        </choose>
    </where>
</select>

trim(set where)

sql片段

有的时候,我们可以把一些功能的部分抽取出来,方便复用!

1、使用sql标签抽取公共部分

2、使用include调用

foreach