ORM框架-mybatis回顾一

最近在读spring的源码时候发现mybatis-spring的最新版本的整合spring的方式和之前有点不一样,之前是通过@MapperScan为入口,通过Import注解注入MapperScannerRegistrar.class类,该类实现了ImportBeanDefinitionRegistrar接口,在spring扫描完包实例化BeanDefinition实例后mybatis才开始通过继承spring扫描器的方式开始工作,而新版是通过实现BeanDefinitionRegistryPostProcessor接口。但是最近写项目一直在用spring data jpa,所以打算趁此把mybatis再好好回顾一遍,然后看看mybatis的源码。目前打算可能写3篇mybatis应用的博客,入门、连接池、关系映射。

1.mybatis的入门demo

1.1 首先先创建数据库:

CREATE DATABASE IF NOT EXISTS db_mybatis CHARACTER SET utf8;
-- 创建数据表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(32) NOT NULL COMMENT '用户名称',
  `birthday` DATETIME DEFAULT NULL COMMENT '生日',
  `sex` CHAR(1) DEFAULT NULL COMMENT '性别',
  `address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
-- 添加记录
INSERT  INTO `user`(`id`,`username`,`birthday`,`sex`,`address`) VALUES (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小二王','2018-03-02 15:09:37','女','北京金燕龙'),(43,'小二王','2018-03-04 11:34:34','女','北京金燕龙'),(45,'传智播客','2018-03-04 12:04:06','男','北京金燕龙'),(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小马宝莉','2018-03-08 11:44:00','女','北京修正');

1.2 创建一个maven应用

构建一个与数据库表字段所对应的User实体类
public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String userAddress;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", userAddress='" + userAddress + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getUserAddress() {
        return userAddress;
    }

    public void setUserAddress(String userAddress) {
        this.userAddress = userAddress;
    }


}
编写UserDao接口
public interface UserDao {
    List<User> findAll();
}
编写UserDao.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">

<select id="findAll" resultType="mybatis.entity.User">
      select * from user
</select>
</mapper>

编写测试类

@Test
    public void MybatisDemoTest throws IOException {
        InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
        SqlSession session = sqlSessionFactory.openSession();
        UserDao userDao = session.getMapper(UserDao.class);
        List<User> list = userDao.findAll();
        list.forEach(System.out::println);
        session.close();
        stream.close();
    }

相关注意事项:
1.UserDao与UserDao.xml需要在同样的全类名路径下,UserDao位于/src/main/java/mybatis/dao/UserDao
UserDao.xml位于src/main/resource/mybatis/dao/UserDao.xml
这里看上去似乎不在同一个路径下,但是在程序打包后其实会将UserDao.xml也放在classes/mybatis/dao下
2. 数据库的字段名需要与实体类User中的一致,否则查询到结果后无法将结果注入到User的属性中

2.相关配置文件解释

2.1首先解释下mybatis环境配置文件

在这里插入图片描述

<?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>
    <!--环境配置-->
    <!--default代表启用哪个环境的配置,mybatis中可以配置多个环境,官网中有说明-->
    <!--MyBatis can be configured with multiple environments. This helps you to apply your SQL Maps to multiple databases for any number of reasons. For example, you might have a different configuration for your Development, Test and Production environments. Or, you may have multiple production databases that share the same schema, and you’d like to use the same SQL maps for both. There are many use cases.-->

    <!--全局配置-->
    <properties resource="datasource.properties">
        <!--可以在内部配置一些全局信息在当前配置文件中都可以使用,也可以引入外部配置文件
            resource属性:用于指定配置文件的位置,是按照类路径的写法来写,必须存在于类路径下
            url属性:要求按照url的写法来写地址;http//localhost:8080/mybatis/xxxx.properties
                                            file:///D:/mybatis/xxx.properties
        -->
        <!--<property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/db_mybatis"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>-->

    </properties>
    <!--配置别名,指定了别名就不在区分大小写,既User、user都可以 -->
    <typeAliases>
        <!--针对某一个实体类的配置-->
        <!--<typeAlias type="mybatis.entity.User" alias="user"></typeAlias>-->
        <!--用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,也不区分大小写-->
        <package name="mybatis.entity.User"></package>
    </typeAliases>


    <environments default="mysql">
        <environment id="mysql">
            <!--配置事务类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据源(连接池),POOLED-JDBC的数据池实现 -->
            <dataSource type="POOLED">
                <!--配置连接数据库的基本信息-->
                <!--<property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/db_mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>-->
                <!--通过外部文件,value必须与外部文件的key一致-->
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--引入映射文件的位置-->
    <!--
        如果此处是使用注解方式来配置的话,此处应该使用class属性指定被注解的dao全限定类名
    -->
    <mappers>
        <!--单个接口的配置方式,可以通过xml文件配置,也可以根据接口的全限定类路径配置-->
        <!--<mapper resource="mybatis/dao/UserDao.xml"/>-->
        <!--<mapper class="mybatis.dao.UserDao"/>-->
        <!--配置整个包下的接口-->
        <package name="mybatis.dao"/>
    </mappers>
</configuration>
<properties>
    <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/db_mybatis"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </properties>
    <!--相当于配置了全局变量,变量名为property标签中name属性的值,变量的值为property标签中value属性的值,在当前配置文件中可以通过${driver}方式获取到-->
<!-- ======================================================== -->
<properties resource="datasource.properties"></properties>
<!--resource属性:会从classpath目录下去寻找资源
	properties标签还有一个url属性,url属性可以通过:file///  [文件的全路径]方式
	也可以通过http://localhost:8080/app/xxx.properties的方式
-->

2.2UserDao的映射文件

<?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对应的dao的全限定类名-->
<mapper namespace="mybatis.dao.UserDao">
    <select id="findAll" resultType="mybatis.entity.User">
      select * from user
    </select>
    <!--
		在mapper标签中配置的namespace代表的是当前映射文件与哪个接口相关,需要配置接口的全限定类名
		select标签中的id代表所对应接口的方法名需要唯一,resultType代表返回值的类型,因为这个findAll方法是无返回的方法,所以parameterType就不需要配置了
	-->
</mapper>

在mapper配置文件中还有很多标签在下面讲mybatis的基本crud操作的时候会提到,以上只聊一下通用的一些标签

3.mybatis的基本crud操作

3.1mybatis的查询方法

3.1.1 通过id查询User

//接口方法
User findUserById(Integer id);
<!--配置文件中对应的方法-->
<select id="findUserById" parameterType="java.lang.Integer" resultType="mybatis.entity.User">
        select * from user where id=#{xxxxxxxx}
</select>
<!--因为我们在mybatis-config中配置了别名所以这里就不需要写全路径了直接写全名即可,并且mybatis对于基本的几种数据类型本身就配置了别名
Alias	Mapped Type
_byte	byte
_long	long
_short	short
_int	int
_integer	int
_double	double
_float	float
_boolean	boolean
string	String
byte	Byte
long	Long
short	Short
int	Integer
integer	Integer
double	Double
float	Float
boolean	Boolean
date	Date
decimal	BigDecimal
bigdecimal	BigDecimal
object	Object
map	Map
hashmap	HashMap
list	List
arraylist	ArrayList
collection	Collection
iterator	Iterator-->

所以映射文件中可以直接简写

<select id="findUserById" parameterType="int" resultType="user">
   select * from user where id=#{xxxxxxxx}
   <!--这里的id一般这样写可读性更强,但是这个id只是一个占位符随便写什么都可以,博主这里只是为了测试一下-->
   select * from user where id=#{id}
</select>
3.1.2 模糊查询
List<User> findUserByName(String user);
<select id="findUserByName" resultType="user" parameterType="string">
	select * from user where username like '%${value}%'
</select>
<!--这里通过name进行模糊查询有两种写法,就涉及到了面试常问的一个题:mybatis中占位符#和$的区别,这里就简单提一下,等写源码系列的博客会对这个机制进行深入探讨,首先#是预编译的写法,不是对传入的参数进行简单拼接,也就是JDBC中的PreparedStatement,而$是对传入的参数进行拼接,就存在sql注入攻击的问题
相应的这两种不同的方式在外部调用findUserByName的时候也不同,前者需要自己拼接%,而且前者的占位符必须是固定值value,同样的在分析源码的时候再来探讨-->
//采用select * from user where username like '%${value}%'
UserDao userDao = session.getMapper(UserDao.class);
userDao.findUserByName("张");

查询结果的sql语句:select * from user where username like ‘%张%’

//采用select * from user where username like #{username}
UserDao userDao = session.getMapper(UserDao.class);
userDao.findUserByName("%" ++ "%");

查询结果的sql语句:select * from user where username like ?

3.1.2 通过POJO作为参数查询

public class QueryVo {

    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}
List<User> findUserByVo(QueryVo vo);
 <!--根据QueryVo的条件查询用户-->
    <select id="findUserByVo" parameterType="queryVO" resultMap="userMap">
        select * from user where username like #{user.username}
    </select>
 		UserDao userDao = session.getMapper(UserDao.class);
        User user = new User();
        user.setUsername("%a%");
        QueryVo vo = new QueryVo();
        vo.setUser(user);
        userDao.findUserByVo(vo);
	//在本案例中参数比较简单可能看不出效果,但是如果在复杂查询中通过这种方法可以一次性封装多个参数

这里我们没有使用resultType=“user”,而是使用了resultMap
关于resultMap的使用是因为之前我们创建这个项目的时候就约定了数据库的字段名与实体类的成员变量名需要一一对应,但是实际开发过程中可能并不是这样,这里我故意将User实体类中的属性address改为userAddress这时候再去查询数据库返回的值中实体类user的address属性是没有值的,这里有两种解决思路
1.通过别名

select id,username,address as userAddress,sex birthday from user

通过这种方式是最高效的,因为在数据库层面就解决了问题,但是开发效率比较低,另一种方式就是通过自定义的resultMap
2.定义resultMap

<resultMap id="userMap" type="mybatis.entity.User">
        <!--主键字段的对应-->
        <id property="id" column="id"></id>
        <!--非主键字段的对应-->
        <result property="username" column="username"></result>
        <result property="userAddress" column="address"></result>
        <result property="sex" column="sex"></result>
    </resultMap>

3.2mybatis的保存方法

void saveUser(User user);
<insert id="saveUser" parameterType="mybatis.entity.User">
        insert into user(username,userAddress,sex,birthday) values(#{username},#{userAddress},#{sex},#{birthday});
</insert>
@Test
    public void testSave() throws IOException {
        User user = new User();
        user.setUserAddress("杭州");
        user.setBirthday(new Date());
        user.setSex("male");
        user.setUsername("二狗");
        InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
        SqlSession session = sqlSessionFactory.openSession();
        UserDao userDao = session.getMapper(UserDao.class);
        userDao.saveUser(user);
        //在保存完以后获取user的id永远返回0,如果要在程序中获取id需要在mapper映射文件上加上一些配置
        System.out.println(user.getId());
        //需要手动提交事务
        //这里注释如果不打开那么对于删除、保存、更新操作是不会同步到数据库的,因为JDBC默认是不会提交事务的
        //session.commit();
        session.close();
        stream.close();
    }

<!--在sql语句中有一个查询最后一次要保存的记录的id;其实就是本次,select last_insert_id();但是如果只执行这一条语句也是无法获取到id的,需要执行insert操作后执行这个语句-->
    <insert id="saveUser" parameterType="mybatis.entity.User">
        <!-- keyProperty代表要返回的值的名称,需要与实体类对应;order:取值为AFTER代表插入后的行为,如果取BEFORE就永远返回0,不知道有什么场景;resultType代表返回值类型;keyColumn是数据库的id的字段名-->
        <!--其实有些字段是可以省略的-->
        <selectKey keyProperty="id" keyColumn="id" order="AFTER" resultType="int">
            select last_insert_id();
        </selectKey>
        insert into user(username,userAddress,sex,birthday) values(#{username},#{userAddress},#{sex},#{birthday});
    </insert>

3.3mybatis的更新方法

void updateUser(User user);
<update id="updateUser" parameterType="mybatis.entity.User">
        update user set username=#{username},userAddress=#{userAddress},sex=#{sex},birthday=#{birthday} whenever id=#{id}
    </update>

更新方法只需要注意一点需要手动commit事务

3.4mybatis的删除方法

void updateUser(User user)
 <!--delete from user where id = #{id}-->
    <!--此时传入的参数只有一个int,这个时候其实不用像之前的保存、更新方法那样写id=#{id}一定要对应实体类的属性名,只需要写一个占位符就可以,且占位符可以随便取名-->
    <delete id="deleteUser" parameterType="int">
        delete from user where id=#{uid}
    </delete>

3.5mybatis中使用sql的函数

int findUserCount();
<select id="findUserCount" resultType="int">
        select count(id) from user
</select>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值