Home Mybatis学习

0 18

有一个需求,需要新增一个查询接口,我原本是直接写到xml文件里面的,一切都好

但是组长在代码CR的时候说,该xml文件是Mybatis-Plus自动生成的,如果我们将自定义SQL写在其中

那么,下次当我们再使用Mybatis-Plus自动生成时,会覆盖掉我们的自定义SQL

所以需要将自定义SQL写在自己定义的xml文件中 或者 采用注解的方式

当我写好@select之后,却发现查询出来始终为空,而在Navicat中 直接复制SQL查询是可以查到的,因此我怀疑是结果映射的问题。

而网上真他妈不好搜Mybatis注解的结果映射,这玩意儿真的很反人类,还好,最好搜到了相关资料

(36条消息) @Results与@ResultMap使用_蜗牛-的博客-CSDN博客_@resultmap

然后我直接 @ResultMap(“BaseResultMap”) 注:BaseResultMap在该Dao对应的xml文件中有定义,故可行。

0 48

sql是正确的,但是商品这个对象只有price(价格),stock(存量)是有值的,其他的都没有.

我自己的解决办法是:不使用resultType(问题很多),而是使用resultMap.

下面是我的应用场景和解决思路.

/**
* 商品
*/
public class Goods {
/**
* 商品id
*/
private Integer goodsId;
/**
* 商品名称
*/
private String goodsName;
/**
* 商品所属商店的id
*/
private Integer shId;
/**
* 商品销量
*/
private Integer sales;
/**
* 商品参考价格
*/
private Double priceRefer;
/**
* 价格
*/
private Double price;
/**
* 库存量
*/
private Integer stock;

/**
* 商品的状态吗
*/
private GoodsEnum goodsEnum;
//getter,setter
}
“goodsId”: null,
“goodsName”: null,
“shId”: null,
“sales”: 1,
“priceRefer”: null,
“price”: 9.9,
“stock”: 1,
“goodsEnum”: null
首先,查出来结果了,并且sql语句没有报错,排除数据库层没有查出来结果。

查看相关的逻辑代码后进行debug,定位错误位置在Mybatis的返回值问题上.

然后在百度的启发下,发现,对象中有值的属性都在数据库中对应的列名中没有”_”,所有为null的列名中都包含下划线.

然后发现自己使用的是ResultType=”cn.edu.bean.Goods”,于是改成我写好的一个映射resultMap(resultMap=”BaseResultMap”)即可。

<resultMap id=”BaseResultMap” type=”cn.edu.zzuli.bean.Goods”>
<id column=”goods_id” property=”goodsId” jdbcType=”INTEGER”/>
<result column=”goods_name” property=”goodsName” jdbcType=”VARCHAR”/>
<result column=”sh_id” property=”shId” jdbcType=”INTEGER”/>
<result column=”sales” property=”sales” jdbcType=”INTEGER”/>
<result column=”price_refer” property=”priceRefer” jdbcType=”DOUBLE”/>
<result column=”price” property=”price” jdbcType=”DOUBLE”/>
<result column=”stock” property=”stock” jdbcType=”INTEGER”/>
</resultMap>
我猜想:应该是resultType对小写驼峰命名法和数据库的下划线命名法适应的不太好,才会出现这个问题。
当然有人如果知道答案,希望可以在评论留言,一起学习一起进步。

.当然也有可能是以下这种情况:sql语句中使用了别名机制,导致mybatis没有办法区分那个是对的(猜测)。

转载过来供大家参考。

<resultMap id=”BaseResultMap” type=”com.trhui.ebook.dao.model.MerchantUser”>
<id column=”MU_ID” jdbcType=”BIGINT” property=”muId”/>
<result column=”USER_ID” jdbcType=”BIGINT” property=”userId”/>
<result column=”MERCHANT_NO” jdbcType=”VARCHAR” property=”merchantNo”/>
<result column=”USER_PHONE” jdbcType=”VARCHAR” property=”userPhone”/>
<result column=”GRANTED” jdbcType=”VARCHAR” property=”granted”/>
<result column=”CREATE_DATE” jdbcType=”TIMESTAMP” property=”createDate”/>
<result column=”MERCHANT_USER_ID” jdbcType=”VARCHAR” property=”merchantUserId”/>
<result column=”STATUS” jdbcType=”VARCHAR” property=”status”/>
<result column=”ENTE_USER_NO” jdbcType=”VARCHAR” property=”enteUserNo”></result>
</resultMap>

<sql id=”Base_Column_List”>
MU_ID muId,
USER_ID userId,
MERCHANT_NO merchantNo,
USER_PHONE userPhone,
GRANTED granted,
CREATE_DATE createDate,
MERCHANT_USER_ID merchantUserId,
ENTE_USER_NO enteUserNo,
STATUS status
</sql>
<select id=”selectByPrimaryKey” parameterType=”java.lang.Long” resultMap=”BaseResultMap”>
select
<include refid=”Base_Column_List”/>
from merchant_user
where MU_ID = #{muId,jdbcType=BIGINT}
</select>
如果返回的对象是resultMap 那么就不要给字段加别名了,问题就是出在这里,将字段别名去了就OK;

如果要给字段加别名,那么你就直接返回该对象就好了,路径要写全,如:resultType=”com.trhui.ebook.dao.model.MerchantUser”

而不是返回resultMap=”BaseResultMap”

 

补充:也可能是使用了Mabatis – Plus 而没有表示主键!https://blog.csdn.net/YiWangJiuShiXingFu/article/details/108378299

 

一、原因
mybatis_plus 默认会使用 “id” 为主键字段,如果数据库的主键字段不是“id”的话,使用mybatis-plus中的 selectById ,getById 方法查询数据是查询不出来的

二、解决
在实体类的主键字段加上@TableId(value =“数据库你的主键字段”)注解即可
例如我的是product_id为主键。

利用SQL注入漏洞登录后台

所谓SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令,比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击

sql注入

什么时候最易受到sql注入攻击

当应用程序使用输入内容来构造动态sql语句以访问数据库时,会发生sql注入攻击。如果代码使用存储过程,而这些存储过程作为包含未筛选的用户输入的 字符串来传递,也会发生sql注入。sql注入可能导致攻击者使用应用程序登陆在数据库中执行命令。如果应用程序使用特权过高的帐户连接到数据库,这种问 题会变得很严重。在某些表单中,用户输入的内容直接用来构造动态sql命令,或者作为存储过程的输入参数,这些表单特别容易受到sql注入的攻击。而许多 网站程序在编写时,没有对用户输入的合法性进行判断或者程序中本身的变量处理不当,使应用程序存在安全隐患。这样,用户就可以提交一段数据库查询的代码, 根据程序返回的结果,获得一些敏感的信息或者控制整个服务器,于是sql注入就发生了。

如何防止SQL注入

归纳一下,主要有以下几点:

1.永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和

双”-“进行转换等。

2.永远不要使用动态拼装sql,可以使用参数化的sql或者直接使用存储过程进行数据查询存取。

3.永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。

4.不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。

5.应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装

6.sql注入的检测方法一般采取辅助软件或网站平台来检测,软件一般采用sql注入检测工具jsky,网站平台就有亿思网站安全平台检测工具。

例子一、SQL注入实例详解(以上测试均假设服务器未开启magic_quote_gpc)https://blog.csdn.net/forest_fire/article/details/50944708

1) 前期准备工作

先来演示通过SQL注入漏洞,登入后台管理员界面

首先,创建一张试验用的数据表:

CREATE TABLE `users` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`username` varchar(64) NOT NULL,

`password` varchar(64) NOT NULL,

`email` varchar(64) NOT NULL,

PRIMARY KEY (`id`),

UNIQUE KEY `username` (`username`)

) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;

添加一条记录用于测试:

INSERT INTO users (username,password,email)

VALUES(‘MarcoFly’,md5(‘test’),’marcofly@test.com’);

接下来,贴上登录界面的源代码:

<html>
<head>
<title>Sql注入演示</title>
<meta http-equiv=”content-type” content=”text/html;charset=utf-8″>
</head>

<body >
<form action=”validate.php” method=”post”>
<fieldset >
<legend>Sql注入演示</legend>
<table>
<tr>
<td>用户名:</td>
<td><input type=”text” name=”username”></td>
</tr>
<tr>
<td>密&nbsp;&nbsp;码:</td>
<td><input type=”text” name=”password”></td>
</tr>
<tr>
<td><input type=”submit” value=”提交”></td>
<td><input type=”reset” value=”重置”></td>
</tr>
</table>
</fieldset>
</form>
</body>
</html>

附上效果图:

\
  当用户点击提交按钮的时候,将会把表单数据提交给validate.php页面,validate.php页面用来判断用户输入的用户名和密码有没有都符合要求(这一步至关重要,也往往是SQL漏洞所在)

代码如下:

<html>
<head>
<title>登录验证</title>
<meta http-equiv=”content-type” content=”text/html;charset=utf-8″>
</head>

<body>
<?php

$conn=@mysql_connect(“localhost”,’root’,”) or die(“数据库连接失败!”);;

mysql_select_db(“injection”,$conn) or die(“您要选择的数据库不存在”);

$name=$_POST[‘username’];

$pwd=$_POST[‘password’];

$sql=”select * from users where username=’$name’ and password=’$pwd'”;

$query=mysql_query($sql);

$arr=mysql_fetch_array($query);

if(is_array($arr)){

header(“Location:manager.php”);

}else{

echo “您的用户名或密码输入有误,<a href=\”Login.php\”>请重新登录!</a>”;

}

?>
</body>
</html>

注意到了没有,我们直接将用户提交过来的数据(用户名和密码)直接拿去执行,并没有实现进行特殊字符过滤,待会你们将明白,这是致命的。

代码分析:如果,用户名和密码都匹配成功的话,将跳转到管理员操作界面(manager.php),不成功,则给出友好提示信息。

登录成功的界面:

\
  登录失败的提示:

\
  到这里,前期工作已经做好了,接下来将展开我们的重头戏:SQL注入

2) 构造SQL语句

填好正确的用户名(marcofly)和密码(test)后,点击提交,将会返回给我们“欢迎管理员”的界面。

select * from users where username=’marcofly’ and password=md5(‘test’)

很明显,用户名和密码都和我们之前给出的一样,肯定能够成功登陆。但是,如果我们输入一个错误的用户名或密码呢?很明显,肯定登入不了吧。恩,正常情况下是如此,但是对于有SQL注入漏洞的网站来说,只要构造个特殊的“字符串”,照样能够成功登录。

比如:在用户名输入框中输入:’ or 1=1#,密码随便输入,这时候的合成后的SQL查询语句为:

select * from users where username=” or 1=1#’ and password=md5(”)

语义分析:“#”在mysql中是注释符,这样井号后面的内容将被mysql视为注释内容,这样就不会去执行了,换句话说,以下的两句sql语句等价:

select * from users where username=” or 1=1#’ and password=md5(”)

等价于

select * from users where username=” or 1=1

SQL注入采用的’ OR 1=1 # 是什么意思呢?

最后一个#号有什么意义呢?
SELECT * FROM test WHERE name=” OR 1=1 #’ AND age=’20’
这后面写的 #’ 是什么意思呢? 求指教
# 可以注释掉后面的一行SQL代码

相当于去掉了一个where条件

MySQL 注释, 过滤掉后面的SQL语句,使其不起作用

因为1=1永远是都是成立的,即where子句总是为真,将该sql进一步简化之后,等价于如下select语句:

select * from users 没错,该sql语句的作用是检索users表中的所有字段

小技巧:一个经构造后的sql语句竟有如此可怕的破坏力,相信你看到这后,开始对sql注入有了一个理性的认识了吧~

有漏洞的脚本才有机会给你攻击,比如一个带参数的删除脚本a.asp?action=del&id=2你可以改为a.asp?action=del&id=2 or 1这样就有可能删除全部数据——sql注入就是通过类似的手段来破坏数据

尝试:在我的毕业设计首页搜索中输入【123′ or 1=(select count(1) from tb_users)–】会查询不出人和数据  并且不会报错   通过这样可以判断是否存在表tb_users

原文地址:http://www.cnblogs.com/rush/archive/2011/12/31/2309203.html

public synchronized Integer kill(Integer id, HttpSession session) {
    //根据 商品 ID 校验库
    Stock stock = checkStock(id);
    LOGGER.info("商品的名称为:" + stock.getName());
    LOGGER.info("商品的已售为:" + stock.getSaled());
    LOGGER.info("商品的库存为:" + stock.getCount());
    //更新库存
    updateSale(stock);
    //创建订单
    return createOrder(stock,session);
}

尝试给 kill 方法加悲观锁解决超卖

坑点:synchronized 加在业务层,而业务层加了事务控制,加入事务控制会导致线程同步(事务的)
而synchronized 也有一个线程同步,事务的线程同步的范围比synchronized 的大

当我们的synchronized 代码块确实能保证线程排队走,但是synchronized 代码块结束之后,事务还没结束,这个线程已经把锁释放了
但事务还未结束,事务还没提交,此时下一个线程已经来了,来了之后,此时事务开始提交,下一个线程执行了,执行的时候数据库也跟着提交
这样就有可能出现多次提交的问题,所以即使在业务层加上synchronized 也会出现超卖问题

解决方案:
1. 去掉事务
2. synchronized 放在我们的Controller  调用处,保证synchronized 的线程同步范围比事务的要大

Springboot整合Mybatis的步骤:

1.数据源配置:在application.properties里面

#配置数据源
spring.datasource.url=jdbc:mysql://localhost:3306/transactional_springboot?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456789
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

2.(前提是导入依赖)实体类,Mapper接口的编写

@Repository
public interface UserMapper {
    void insertUser(User user);
}
@Data
@Repository
public class User {
    private String name;
    private int id;
}

3.Mapper.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">

<mapper namespace="com.yang.mapper.UserMapper">

    <insert id="insertUser" parameterType="com.yang.pojo.User">
          insert into  user(id,name) values (#{id},#{name});
    </insert>
</mapper>
4.Controller层编写
@RestController
public class InsertUserController {

    @Autowired
    UserMapper userMapper;
    @Autowired
    User user;
    @PostMapping("/insert")
    public void insert(){
        user.setId(10);
        user.setName("First");
        userMapper.insertUser(user);
    }
}
5.测试类编写
@SpringBootTest(classes = Application.class)
public class Test {

    @Autowired
    InsertUserController insertUserController;

    @org.junit.jupiter.api.Test
    public void go(){
        insertUserController.insert();
    }
}

坑点:Mapper接口文件不仅要有@Repository注解(标记为Bean,给Spring管理)还要有@Mapper注解才可 !!!否则会报
Field userMapper in com.yang.controller.InsertUserController required a bean of type 'com.yang.mapper.UserMapper' that could not be found.
找不到Mapper文件错误

package com.yang.dao;

import com.yang.mapper.DepartmentMapper;
import com.yang.pojo.Department;
import com.yang.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

//员工Dao
//标识为Bean
@Repository
public class EmployeeDao {

    //模拟数据库中的数据
    private static Map<Integer, Employee> employees = null;
    //员工有所属部门,注入部门
    @Autowired
    DepartmentMapper departmentMapper;

    static {
        employees = new HashMap<Integer, Employee>();//创建一个部门表

        employees.put(1001, new Employee(1001, "AA", "A1606598203@qq.com", 0, new Department(101, "教学部")));
        employees.put(1002, new Employee(1002, "BB", "B1606598203@qq.com", 1, new Department(102, "市场部")));
        employees.put(1003, new Employee(1003, "CC", "C1606598203@qq.com", 0, new Department(103, "教研部")));
        employees.put(1004, new Employee(1004, "DD", "D1606598203@qq.com", 1, new Department(104, "运营部")));
        employees.put(1005, new Employee(1005, "EE", "E1606598203@qq.com", 0, new Department(105, "后勤部")));
    }

    //增加一个员工,主键自增!
    private static Integer initID = 1006;

    //增加一个员工
    public void save(Employee employee) {
        if(employee.getId()==null){
            employee.setId(initID++);
        }

        employee.setDepartment(departmentMapper.getDepartmentById(employee.getDepartment().getId()));

        employees.put(employee.getId(),employee);
    }

    //查询全部员工信息
    public Collection<Employee> getAll(){

        return employees.values();
    }

    //通过ID查询员工
    public Employee getEmployeeById(Integer id){

        return employees.get(id);
    }

    //通过ID删除员工
    public void delete(Integer id){

        employees.remove(id);
    }

}

1.导入依赖(Mybatis)

<!--        mybatis自带的-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

2.编写配置文件连接数据库(application.properties)
spring.datasource.username=root
spring.datasource.password=123456789
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

以下为具体测试:

3.编写实体类(Pojo)

import lombok.Data;

@Data
public class User {
    private int id;
    private String name;
    private String pwd;
}
4.IDEA连接数据库

5.实体类写好编写Mapper接口

或者
6.在Mapper.xml中编写sql语句
<mapper namespace="com.yang.dao.UserMapper">

    <select id="getUserList" resultType="PoJo.User">
        select * from mysql.mybatisuser
    </select>

    <select id="getUserLike" parameterType="map" resultType="PoJo.User">
        select * from mysql.mybatisuser where name like #{value};
    </select>


    <select id="getUserById" parameterType="int" resultType="PoJo.User">
        select * from mysql.mybatisuser where id = #{userid};
    </select>

    <select id="getUserById2" parameterType="map" resultType="PoJo.User">
        select * from mysql.mybatisuser where id = #{id};
    </select>

    <insert id="addUser" parameterType="PoJo.User">
        insert into mysql.mybatisuser (id,name,pwd) values (#{id},#{name},#{pwd});
    </insert>
<!--    <insert id="addUser2" parameterType="map">-->
<!--        insert into mysql.mybatisuser (id,name,pwd) values (#{userid},#{username},#{password});-->
<!--    </insert>-->
    <update id="updateUser" parameterType="PoJo.User">
        update mysql.mybatisuser set name = #{name},pwd = #{pwd} where id = #{id};
    </update>

    <delete id="deleteUser" parameterType="int">
        delete from mysql.mybatisuser where id = #{id};
    </delete>
</mapper>

7.Springboot整合Mybatis
#整合Mybatis
mybatis.type-aliases-package=com.yang.pojo   //包下实体类起别名
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml    //Mapper的地址
其他配置也写在这里

8.编写Controller,调用Dao层(Mapper接口)
@RestController
public class UserController {

    @Autowired
    private UserMapper userMapper;

    @GetMapping("userList")
    public List<User> queryUserList(){
        List<User> userList = userMapper.queryUserList();
        for (User user : userList) {
            System.out.println(user);
        }
        return userList;
    }
}

Mybatis中别名的作用:

在mybatis-config.xml配置别名如下:

<!– 别名 –>
<typeAliases>
<package name=”cn.itcast.core.bean”/>
</typeAliases>

它的作用是让Mapper.xml中的参数找到对应类,如下面parameterType=”TestTb”>,如果没有配置别名,则要改为parameterType=”cn.itcast.core.bean.TestTb”>,

配置别名首先当然要保证pojo实体,在一个包下面如cn.itcast.core.bean,这样配置别名后,都可找到对应的参数;

<mapper namespace=”cn.itcast.core.dao.TestTbDao”>

<!– 保存 –>
<insert id=”insertTestTb” parameterType=”TestTb”>
insert into test_tb
(id,name,birthday)
values
(#{id},#{name},#{birthday})
</insert>

</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">

<mapper namespace="com.yang.dao.UserMapper">