mybatis基础

1、mybatis简单例子

MyBatis是支持普通SQL查询存储过程高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数库中的记录.

mybatis和hibernate区别:mybatis是用sql语句生成对象,hibernate是以对象生成sql语句。

1、添加依赖

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

2、添加mybatis.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" />
                <property name="url" value="jdbc:mysql://localhost:3306/test" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="mybatis/userMapper.xml"/>
    </mappers>
</configuration>

3、实体类

@Data
public class User {
    private int id;
    private String name;
    private int age;
    //get,set方法
}

4、定义userMapper接口

public interface UserMapper {
    User getUser(int i);
}

5、定义操作数据库的sql映射文件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">
<mapper namespace="com.webservice.webserver.mapper.UserMapper">
    <select id="getUser" parameterType="int" resultType="com.webservice.webserver.model.User">
		SELECT *
		FROM users where id =#{id}
	</select>
</mapper>

6、测试文件


    @Test
    public void test() throws IOException {
        String resource="mybatis.xml";
        //读取配置文件
        Reader reader= Resources.getResourceAsReader(resource);
        //获取会哈工厂
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
        SqlSession sqlSession=sqlSessionFactory.openSession();
        //查询
        String sql="com.webservice.webserver.mapper.UserMapper.getUser";
        //调用API查询
        User user=sqlSession.selectOne(sql,1);
        System.out.println(user.toString());
    }

输出正确信息即为正确。

2、mybatisSQL注入

直接上例子比较直观。

1、创建表和测试数据

create table user_table(  
    id      int Primary key,  
    username    varchar(30),  
    password    varchar(30)  
);  
insert into user_table values(1,'test-1','12345');  
insert into user_table values(2,'test-2','12345');  

2、jdbc进行加载模拟一个登录过程

    @Test
    public void  test2()  {
        try {
            String username = "test-1";
            String password = "12345";
            String sql = "SELECT id,username FROM user_table WHERE " + "username='" + username + "'AND " + "password='"
                    + password + "'";
            Class.forName("com.mysql.jdbc.Driver");
            Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
            PreparedStatement stat = con.prepareStatement(sql);
            System.out.println(stat.toString());
            ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                String id = rs.getString(1);
                String name = rs.getString(2);
                System.out.println("id:" + id + "---name:" + name);
            }
        } catch (Exception e){
            e.printStackTrace();
        }

    }

可以查到test-1用户的信息。

3、如果test-1的密码为1234567呢?很明显是查不到的。但如果是用户名或者密码为' or 1='1(这里不一定是这个值,只要保证恒等就可)。神奇的事情发生了,查到了????

因此有的人就说这样设置传入SQL的参数。

            String sql = "SELECT id,username FROM user_table WHERE username=? AND password=?";
            Class.forName("com.mysql.jdbc.Driver");
            Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
            PreparedStatement stat = con.prepareStatement(sql);
            stat.setString(1,username);
            stat.setString(2,password);

这样为什么能呢?

看看里面真正执行的SQL

SELECT id,username FROM user_table WHERE username='test-1' AND password='1234556 \' or 1=\'1'

是不是恍然大悟?

但是如果将传入的参数改为包含SQL注解的呢?比如

String username = "' or 1='1 -- test-1";
String username = " ' or 1='1';";

预编译也能解决,只是提一下这种情况。。。

2.1 mybatis $和#

动态 sql mybatis 的主要特性之一,在 mapper 中定义的参数传到 xml 中之后,在查询之前 mybatis 会对其进行动态解析。mybatis 为我们提供了两种支持动态 sql 的语法:#{} 以及 ${}

在下面的语句中,如果 username 的值为 zhangsan,则两种方式无任何区别:

select * from user where name = #{name};

select * from user where name = ${name};

其解析之后的结果均为

select * from user where name = 'zhangsan';

 但是 #{} ${} 在预编译中的处理是不一样的。#{} 在预处理时,会把参数部分用一个占位符 ? 代替,变成如下的 sql 语句:

select * from user where name = ?;

${} 则只是简单的字符串替换,在动态解析阶段,该 sql 语句会被解析成

select * from user where name = 'zhangsan';

以上,#{} 的参数替换是发生在 DBMS 中,而 ${} 则发生在动态解析过程中。

那么,在使用过程中我们应该使用哪种方式呢?

答案是,优先使用 #{}。因为 ${} 会导致 sql 注入的问题。看下面的例子:

 select * from ${tableName} where name = #{name}

在这个例子中,如果表名为

 user; delete user; -- 

  则动态解析之后 sql 如下:

select * from user; delete user; -- where name = ?;

  --之后的语句被注释掉,而原本查询用户的语句变成了查询所有用户信息+删除用户表的语句,会对数据库造成重大损伤,极大可能导致服务器宕机。

但是表名用参数传递进来的时候,只能使用 ${} ,具体原因可以自己做个猜测,去验证。这也提醒我们在这种用法中要小心sql注入的问题。

例子:

在简单例子和前面注入的基础上。

添加UserTable类

@Data
public class UserTable {
    private int id;
    private String userName;
    private String passWord;

}

UserTableMapper.java

public interface UserTableMapper {
    UserTable login(UserTable userTable);
}

userTableMapper.xml记得引入到mybatis.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">
<mapper namespace="com.webservice.webserver.mapper.UserTableMapper">
    <select id="login" parameterType="com.webservice.webserver.model.UserTable"
            resultType="com.webservice.webserver.model.UserTable">
		SELECT id ,username as userName FROM user_table WHERE
		username=${userName} AND password=${passWord}
	</select>

</mapper>

test:

 @Test
    public void test3(){
        try{
            String resource = "mybatis.xml";
            // 读取配置文件
            Reader reader = Resources.getResourceAsReader(resource);
            // 获取会话工厂
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
            SqlSession openSession = sqlSessionFactory.openSession();
            // 查询
            String sql = "com.webservice.webserver.mapper.UserTableMapper.login";
            // 调用api查询
            UserTable userTable = new UserTable();
            userTable.setUserName("''  OR 1=1 -- ");
            userTable.setPassWord("12345");
            List<UserTable> listUserTable = openSession.selectList(sql, userTable);
            for (UserTable ub : listUserTable) {
                System.out.println(ub.getUserName());
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }

使用${}后执行以上测试程序居然能得到结果??开启日志调试后得到SQL执行语句:

14:10:55.328 [main] DEBUG com.webservice.webserver.mapper.UserTableMapper.login - ==>  Preparing: SELECT id ,username as userName FROM user_table WHERE username='' OR 1=1 -- AND password=12345 
14:10:55.354 [main] DEBUG com.webservice.webserver.mapper.UserTableMapper.login - ==> Parameters: 
14:10:55.373 [main] DEBUG com.webservice.webserver.mapper.UserTableMapper.login - <==      Total: 2

可以看到${}仅仅是简单的字符串替换罢了,很容易就被SQL注入了。

如果使用#{}呢?

14:16:49.407 [main] DEBUG com.webservice.webserver.mapper.UserTableMapper.login - ==>  Preparing: SELECT id ,username as userName FROM user_table WHERE username=? AND password=? 
14:16:49.434 [main] DEBUG com.webservice.webserver.mapper.UserTableMapper.login - ==> Parameters: ''  OR 1=1 -- (String), 12345(String)
14:16:49.449 [main] DEBUG com.webservice.webserver.mapper.UserTableMapper.login - <==      Total: 0

其实就是预编译了而已和jdbc加载的没什么区别。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值