如果使用了parameterType,只能传入一个参数,如果要传入多个参数,可以放到list、map中传入
<!-- 传入list --> <select id="xxx" parameterType="list" resultType="xxx"> <!-- 默认的列表名是list,可通过下标取值 --> SELECT * FROM tb_user WHERE name=#{list[2]} </select> <!-- 传入对象 --> <select id="xxx" parameterType="user" resultType="xxx"> <!-- 通过成员变量名直接取,会自动调用对应的getter方法 --> SELECT * FROM tb_user WHERE name=#{name} </select> <!-- 传入map --> <select id="xxx" parameterType="map" resultType="xxx"> <!-- 通过key直接取 --> SELECT * FROM tb_user WHERE name=#{name} </select> 如果不使用parameterType,可直接传入多个参数 public interface UserMapper{ User findUser(Integer id, String name); <!-- 不要parameterType --> <select id="findUser" resultType="user"> SELECT * FROM tb_user WHERE id=#{id} and name=#{name} </select> 参数名不同的问题 @Param可解决mapper接口、xml映射文件参数名不同的问题 public interface UserMapper{ // 通过@Param绑定xml中的name变量 User findUser(Integer id, @Param("name") String username); <select id="findUser" resultType="user"> SELECT * FROM tb_user WHERE id=#{id} and name=#{name} </select> #{ }、${ }的区别 #{ }用于取值,${ }除了有#{ }的功能,还可以拼接字符串,但使用${ }拼接字符串有sql注入的风险,一律禁用${ }。 以模糊查询为例 <select id="xxx" parameterType="string" resultType="user"> SELECT * FROM tb_user WHERE name LIKE '%${name}%' </select> <select id="xxx" parameterType="string" resultType="user"> <!-- 使用sql的concat()函数拼接字符串,使用#{}取值 --> SELECT * FROM tb_user WHERE name LIKE concat('%',#{name},'%') </select> resultMap的使用 <resultMap> 用于自定义结果集映射 <!-- resultType不能和resultMap同时使用,fetchSize指定返回的记录数,未指定时默认返回所有满足要求的记录 --> <select id="" parameterType="" resultMap="userMap" fetchSize="10"> </select> <!-- type指定po --> <resultMap id="userMap" type="user"> <!-- 默认映射规则是同名映射,我们只需指定名称不一致的字段,主键字段用id指定,普通字段用result指定 --> <id property="id" column="user_id"/> <result property="username" column="user_name"/> </resultMap> <!-- resultMap属性指定要使用的<resultMap> --> <select id="" parameterType="" resultMap="userMap"> </select> 此外,<resultMap>还有2个常用子元素<association>、<collection>,用于关联映射。 关系数据库中,多表之间有三种关联关系 一对一:一张身份证对应一个人 <=> 一个人也只对应一张身份证 一对多:一个用户可以有多个订单 <=> 这多个订单都属于同一个用户 多对多:一个订单可以包含多种商品 <=> 一种商品可以属于多个订单 设计数据库时如何处理三种关联关系? 一对一:在一方引入另一方的主键作为外键 一对多:在多的一方,引入一的一方的主键作为外键 多对多:新建一张中间表,引入2张表的主键作为外键,可以使用这2个外键作为中间表的联合主键,也可以新建id字段作为中间表的主键 java如何处理、描述三种关联关系? // 一对一 class A{ B b; class B{ A a; // 一对多,A是一,B是多 class A{ List<B> b; class B{ A a; // 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射? <select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<!-- 传入list --> <select id="xxx" parameterType="list" resultType="xxx"> <!-- 默认的列表名是list,可通过下标取值 --> SELECT * FROM tb_user WHERE name=#{list[2]} </select> <!-- 传入对象 --> <select id="xxx" parameterType="user" resultType="xxx"> <!-- 通过成员变量名直接取,会自动调用对应的getter方法 --> SELECT * FROM tb_user WHERE name=#{name} </select> <!-- 传入map --> <select id="xxx" parameterType="map" resultType="xxx"> <!-- 通过key直接取 --> SELECT * FROM tb_user WHERE name=#{name} </select> 如果不使用parameterType,可直接传入多个参数
如果不使用parameterType,可直接传入多个参数
public interface UserMapper{ User findUser(Integer id, String name); <!-- 不要parameterType --> <select id="findUser" resultType="user"> SELECT * FROM tb_user WHERE id=#{id} and name=#{name} </select> 参数名不同的问题 @Param可解决mapper接口、xml映射文件参数名不同的问题 public interface UserMapper{ // 通过@Param绑定xml中的name变量 User findUser(Integer id, @Param("name") String username); <select id="findUser" resultType="user"> SELECT * FROM tb_user WHERE id=#{id} and name=#{name} </select> #{ }、${ }的区别 #{ }用于取值,${ }除了有#{ }的功能,还可以拼接字符串,但使用${ }拼接字符串有sql注入的风险,一律禁用${ }。 以模糊查询为例 <select id="xxx" parameterType="string" resultType="user"> SELECT * FROM tb_user WHERE name LIKE '%${name}%' </select> <select id="xxx" parameterType="string" resultType="user"> <!-- 使用sql的concat()函数拼接字符串,使用#{}取值 --> SELECT * FROM tb_user WHERE name LIKE concat('%',#{name},'%') </select> resultMap的使用 <resultMap> 用于自定义结果集映射 <!-- resultType不能和resultMap同时使用,fetchSize指定返回的记录数,未指定时默认返回所有满足要求的记录 --> <select id="" parameterType="" resultMap="userMap" fetchSize="10"> </select> <!-- type指定po --> <resultMap id="userMap" type="user"> <!-- 默认映射规则是同名映射,我们只需指定名称不一致的字段,主键字段用id指定,普通字段用result指定 --> <id property="id" column="user_id"/> <result property="username" column="user_name"/> </resultMap> <!-- resultMap属性指定要使用的<resultMap> --> <select id="" parameterType="" resultMap="userMap"> </select> 此外,<resultMap>还有2个常用子元素<association>、<collection>,用于关联映射。 关系数据库中,多表之间有三种关联关系 一对一:一张身份证对应一个人 <=> 一个人也只对应一张身份证 一对多:一个用户可以有多个订单 <=> 这多个订单都属于同一个用户 多对多:一个订单可以包含多种商品 <=> 一种商品可以属于多个订单 设计数据库时如何处理三种关联关系? 一对一:在一方引入另一方的主键作为外键 一对多:在多的一方,引入一的一方的主键作为外键 多对多:新建一张中间表,引入2张表的主键作为外键,可以使用这2个外键作为中间表的联合主键,也可以新建id字段作为中间表的主键 java如何处理、描述三种关联关系? // 一对一 class A{ B b; class B{ A a; // 一对多,A是一,B是多 class A{ List<B> b; class B{ A a; // 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射? <select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
public interface UserMapper{ User findUser(Integer id, String name);
<!-- 不要parameterType --> <select id="findUser" resultType="user"> SELECT * FROM tb_user WHERE id=#{id} and name=#{name} </select> 参数名不同的问题 @Param可解决mapper接口、xml映射文件参数名不同的问题 public interface UserMapper{ // 通过@Param绑定xml中的name变量 User findUser(Integer id, @Param("name") String username); <select id="findUser" resultType="user"> SELECT * FROM tb_user WHERE id=#{id} and name=#{name} </select> #{ }、${ }的区别 #{ }用于取值,${ }除了有#{ }的功能,还可以拼接字符串,但使用${ }拼接字符串有sql注入的风险,一律禁用${ }。 以模糊查询为例 <select id="xxx" parameterType="string" resultType="user"> SELECT * FROM tb_user WHERE name LIKE '%${name}%' </select> <select id="xxx" parameterType="string" resultType="user"> <!-- 使用sql的concat()函数拼接字符串,使用#{}取值 --> SELECT * FROM tb_user WHERE name LIKE concat('%',#{name},'%') </select> resultMap的使用 <resultMap> 用于自定义结果集映射 <!-- resultType不能和resultMap同时使用,fetchSize指定返回的记录数,未指定时默认返回所有满足要求的记录 --> <select id="" parameterType="" resultMap="userMap" fetchSize="10"> </select> <!-- type指定po --> <resultMap id="userMap" type="user"> <!-- 默认映射规则是同名映射,我们只需指定名称不一致的字段,主键字段用id指定,普通字段用result指定 --> <id property="id" column="user_id"/> <result property="username" column="user_name"/> </resultMap> <!-- resultMap属性指定要使用的<resultMap> --> <select id="" parameterType="" resultMap="userMap"> </select> 此外,<resultMap>还有2个常用子元素<association>、<collection>,用于关联映射。 关系数据库中,多表之间有三种关联关系 一对一:一张身份证对应一个人 <=> 一个人也只对应一张身份证 一对多:一个用户可以有多个订单 <=> 这多个订单都属于同一个用户 多对多:一个订单可以包含多种商品 <=> 一种商品可以属于多个订单 设计数据库时如何处理三种关联关系? 一对一:在一方引入另一方的主键作为外键 一对多:在多的一方,引入一的一方的主键作为外键 多对多:新建一张中间表,引入2张表的主键作为外键,可以使用这2个外键作为中间表的联合主键,也可以新建id字段作为中间表的主键 java如何处理、描述三种关联关系? // 一对一 class A{ B b; class B{ A a; // 一对多,A是一,B是多 class A{ List<B> b; class B{ A a; // 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射? <select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<!-- 不要parameterType --> <select id="findUser" resultType="user"> SELECT * FROM tb_user WHERE id=#{id} and name=#{name} </select>
参数名不同的问题 @Param可解决mapper接口、xml映射文件参数名不同的问题 public interface UserMapper{ // 通过@Param绑定xml中的name变量 User findUser(Integer id, @Param("name") String username); <select id="findUser" resultType="user"> SELECT * FROM tb_user WHERE id=#{id} and name=#{name} </select> #{ }、${ }的区别 #{ }用于取值,${ }除了有#{ }的功能,还可以拼接字符串,但使用${ }拼接字符串有sql注入的风险,一律禁用${ }。 以模糊查询为例 <select id="xxx" parameterType="string" resultType="user"> SELECT * FROM tb_user WHERE name LIKE '%${name}%' </select> <select id="xxx" parameterType="string" resultType="user"> <!-- 使用sql的concat()函数拼接字符串,使用#{}取值 --> SELECT * FROM tb_user WHERE name LIKE concat('%',#{name},'%') </select> resultMap的使用 <resultMap> 用于自定义结果集映射 <!-- resultType不能和resultMap同时使用,fetchSize指定返回的记录数,未指定时默认返回所有满足要求的记录 --> <select id="" parameterType="" resultMap="userMap" fetchSize="10"> </select> <!-- type指定po --> <resultMap id="userMap" type="user"> <!-- 默认映射规则是同名映射,我们只需指定名称不一致的字段,主键字段用id指定,普通字段用result指定 --> <id property="id" column="user_id"/> <result property="username" column="user_name"/> </resultMap> <!-- resultMap属性指定要使用的<resultMap> --> <select id="" parameterType="" resultMap="userMap"> </select> 此外,<resultMap>还有2个常用子元素<association>、<collection>,用于关联映射。 关系数据库中,多表之间有三种关联关系 一对一:一张身份证对应一个人 <=> 一个人也只对应一张身份证 一对多:一个用户可以有多个订单 <=> 这多个订单都属于同一个用户 多对多:一个订单可以包含多种商品 <=> 一种商品可以属于多个订单 设计数据库时如何处理三种关联关系? 一对一:在一方引入另一方的主键作为外键 一对多:在多的一方,引入一的一方的主键作为外键 多对多:新建一张中间表,引入2张表的主键作为外键,可以使用这2个外键作为中间表的联合主键,也可以新建id字段作为中间表的主键 java如何处理、描述三种关联关系? // 一对一 class A{ B b; class B{ A a; // 一对多,A是一,B是多 class A{ List<B> b; class B{ A a; // 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射? <select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
@Param可解决mapper接口、xml映射文件参数名不同的问题
public interface UserMapper{ // 通过@Param绑定xml中的name变量 User findUser(Integer id, @Param("name") String username); <select id="findUser" resultType="user"> SELECT * FROM tb_user WHERE id=#{id} and name=#{name} </select> #{ }、${ }的区别 #{ }用于取值,${ }除了有#{ }的功能,还可以拼接字符串,但使用${ }拼接字符串有sql注入的风险,一律禁用${ }。 以模糊查询为例 <select id="xxx" parameterType="string" resultType="user"> SELECT * FROM tb_user WHERE name LIKE '%${name}%' </select> <select id="xxx" parameterType="string" resultType="user"> <!-- 使用sql的concat()函数拼接字符串,使用#{}取值 --> SELECT * FROM tb_user WHERE name LIKE concat('%',#{name},'%') </select> resultMap的使用 <resultMap> 用于自定义结果集映射 <!-- resultType不能和resultMap同时使用,fetchSize指定返回的记录数,未指定时默认返回所有满足要求的记录 --> <select id="" parameterType="" resultMap="userMap" fetchSize="10"> </select> <!-- type指定po --> <resultMap id="userMap" type="user"> <!-- 默认映射规则是同名映射,我们只需指定名称不一致的字段,主键字段用id指定,普通字段用result指定 --> <id property="id" column="user_id"/> <result property="username" column="user_name"/> </resultMap> <!-- resultMap属性指定要使用的<resultMap> --> <select id="" parameterType="" resultMap="userMap"> </select> 此外,<resultMap>还有2个常用子元素<association>、<collection>,用于关联映射。 关系数据库中,多表之间有三种关联关系 一对一:一张身份证对应一个人 <=> 一个人也只对应一张身份证 一对多:一个用户可以有多个订单 <=> 这多个订单都属于同一个用户 多对多:一个订单可以包含多种商品 <=> 一种商品可以属于多个订单 设计数据库时如何处理三种关联关系? 一对一:在一方引入另一方的主键作为外键 一对多:在多的一方,引入一的一方的主键作为外键 多对多:新建一张中间表,引入2张表的主键作为外键,可以使用这2个外键作为中间表的联合主键,也可以新建id字段作为中间表的主键 java如何处理、描述三种关联关系? // 一对一 class A{ B b; class B{ A a; // 一对多,A是一,B是多 class A{ List<B> b; class B{ A a; // 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射? <select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
public interface UserMapper{ // 通过@Param绑定xml中的name变量 User findUser(Integer id, @Param("name") String username);
<select id="findUser" resultType="user"> SELECT * FROM tb_user WHERE id=#{id} and name=#{name} </select> #{ }、${ }的区别 #{ }用于取值,${ }除了有#{ }的功能,还可以拼接字符串,但使用${ }拼接字符串有sql注入的风险,一律禁用${ }。 以模糊查询为例 <select id="xxx" parameterType="string" resultType="user"> SELECT * FROM tb_user WHERE name LIKE '%${name}%' </select> <select id="xxx" parameterType="string" resultType="user"> <!-- 使用sql的concat()函数拼接字符串,使用#{}取值 --> SELECT * FROM tb_user WHERE name LIKE concat('%',#{name},'%') </select> resultMap的使用 <resultMap> 用于自定义结果集映射 <!-- resultType不能和resultMap同时使用,fetchSize指定返回的记录数,未指定时默认返回所有满足要求的记录 --> <select id="" parameterType="" resultMap="userMap" fetchSize="10"> </select> <!-- type指定po --> <resultMap id="userMap" type="user"> <!-- 默认映射规则是同名映射,我们只需指定名称不一致的字段,主键字段用id指定,普通字段用result指定 --> <id property="id" column="user_id"/> <result property="username" column="user_name"/> </resultMap> <!-- resultMap属性指定要使用的<resultMap> --> <select id="" parameterType="" resultMap="userMap"> </select> 此外,<resultMap>还有2个常用子元素<association>、<collection>,用于关联映射。 关系数据库中,多表之间有三种关联关系 一对一:一张身份证对应一个人 <=> 一个人也只对应一张身份证 一对多:一个用户可以有多个订单 <=> 这多个订单都属于同一个用户 多对多:一个订单可以包含多种商品 <=> 一种商品可以属于多个订单 设计数据库时如何处理三种关联关系? 一对一:在一方引入另一方的主键作为外键 一对多:在多的一方,引入一的一方的主键作为外键 多对多:新建一张中间表,引入2张表的主键作为外键,可以使用这2个外键作为中间表的联合主键,也可以新建id字段作为中间表的主键 java如何处理、描述三种关联关系? // 一对一 class A{ B b; class B{ A a; // 一对多,A是一,B是多 class A{ List<B> b; class B{ A a; // 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射? <select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<select id="findUser" resultType="user"> SELECT * FROM tb_user WHERE id=#{id} and name=#{name} </select>
#{ }、${ }的区别 #{ }用于取值,${ }除了有#{ }的功能,还可以拼接字符串,但使用${ }拼接字符串有sql注入的风险,一律禁用${ }。 以模糊查询为例 <select id="xxx" parameterType="string" resultType="user"> SELECT * FROM tb_user WHERE name LIKE '%${name}%' </select> <select id="xxx" parameterType="string" resultType="user"> <!-- 使用sql的concat()函数拼接字符串,使用#{}取值 --> SELECT * FROM tb_user WHERE name LIKE concat('%',#{name},'%') </select> resultMap的使用 <resultMap> 用于自定义结果集映射 <!-- resultType不能和resultMap同时使用,fetchSize指定返回的记录数,未指定时默认返回所有满足要求的记录 --> <select id="" parameterType="" resultMap="userMap" fetchSize="10"> </select> <!-- type指定po --> <resultMap id="userMap" type="user"> <!-- 默认映射规则是同名映射,我们只需指定名称不一致的字段,主键字段用id指定,普通字段用result指定 --> <id property="id" column="user_id"/> <result property="username" column="user_name"/> </resultMap> <!-- resultMap属性指定要使用的<resultMap> --> <select id="" parameterType="" resultMap="userMap"> </select> 此外,<resultMap>还有2个常用子元素<association>、<collection>,用于关联映射。 关系数据库中,多表之间有三种关联关系 一对一:一张身份证对应一个人 <=> 一个人也只对应一张身份证 一对多:一个用户可以有多个订单 <=> 这多个订单都属于同一个用户 多对多:一个订单可以包含多种商品 <=> 一种商品可以属于多个订单 设计数据库时如何处理三种关联关系? 一对一:在一方引入另一方的主键作为外键 一对多:在多的一方,引入一的一方的主键作为外键 多对多:新建一张中间表,引入2张表的主键作为外键,可以使用这2个外键作为中间表的联合主键,也可以新建id字段作为中间表的主键 java如何处理、描述三种关联关系? // 一对一 class A{ B b; class B{ A a; // 一对多,A是一,B是多 class A{ List<B> b; class B{ A a; // 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射? <select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
#{ }用于取值,${ }除了有#{ }的功能,还可以拼接字符串,但使用${ }拼接字符串有sql注入的风险,一律禁用${ }。
以模糊查询为例
<select id="xxx" parameterType="string" resultType="user"> SELECT * FROM tb_user WHERE name LIKE '%${name}%' </select> <select id="xxx" parameterType="string" resultType="user"> <!-- 使用sql的concat()函数拼接字符串,使用#{}取值 --> SELECT * FROM tb_user WHERE name LIKE concat('%',#{name},'%') </select> resultMap的使用 <resultMap> 用于自定义结果集映射 <!-- resultType不能和resultMap同时使用,fetchSize指定返回的记录数,未指定时默认返回所有满足要求的记录 --> <select id="" parameterType="" resultMap="userMap" fetchSize="10"> </select> <!-- type指定po --> <resultMap id="userMap" type="user"> <!-- 默认映射规则是同名映射,我们只需指定名称不一致的字段,主键字段用id指定,普通字段用result指定 --> <id property="id" column="user_id"/> <result property="username" column="user_name"/> </resultMap> <!-- resultMap属性指定要使用的<resultMap> --> <select id="" parameterType="" resultMap="userMap"> </select> 此外,<resultMap>还有2个常用子元素<association>、<collection>,用于关联映射。 关系数据库中,多表之间有三种关联关系 一对一:一张身份证对应一个人 <=> 一个人也只对应一张身份证 一对多:一个用户可以有多个订单 <=> 这多个订单都属于同一个用户 多对多:一个订单可以包含多种商品 <=> 一种商品可以属于多个订单 设计数据库时如何处理三种关联关系? 一对一:在一方引入另一方的主键作为外键 一对多:在多的一方,引入一的一方的主键作为外键 多对多:新建一张中间表,引入2张表的主键作为外键,可以使用这2个外键作为中间表的联合主键,也可以新建id字段作为中间表的主键 java如何处理、描述三种关联关系? // 一对一 class A{ B b; class B{ A a; // 一对多,A是一,B是多 class A{ List<B> b; class B{ A a; // 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射? <select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<select id="xxx" parameterType="string" resultType="user"> SELECT * FROM tb_user WHERE name LIKE '%${name}%' </select> <select id="xxx" parameterType="string" resultType="user"> <!-- 使用sql的concat()函数拼接字符串,使用#{}取值 --> SELECT * FROM tb_user WHERE name LIKE concat('%',#{name},'%') </select>
resultMap的使用 <resultMap> 用于自定义结果集映射 <!-- resultType不能和resultMap同时使用,fetchSize指定返回的记录数,未指定时默认返回所有满足要求的记录 --> <select id="" parameterType="" resultMap="userMap" fetchSize="10"> </select> <!-- type指定po --> <resultMap id="userMap" type="user"> <!-- 默认映射规则是同名映射,我们只需指定名称不一致的字段,主键字段用id指定,普通字段用result指定 --> <id property="id" column="user_id"/> <result property="username" column="user_name"/> </resultMap> <!-- resultMap属性指定要使用的<resultMap> --> <select id="" parameterType="" resultMap="userMap"> </select> 此外,<resultMap>还有2个常用子元素<association>、<collection>,用于关联映射。 关系数据库中,多表之间有三种关联关系 一对一:一张身份证对应一个人 <=> 一个人也只对应一张身份证 一对多:一个用户可以有多个订单 <=> 这多个订单都属于同一个用户 多对多:一个订单可以包含多种商品 <=> 一种商品可以属于多个订单 设计数据库时如何处理三种关联关系? 一对一:在一方引入另一方的主键作为外键 一对多:在多的一方,引入一的一方的主键作为外键 多对多:新建一张中间表,引入2张表的主键作为外键,可以使用这2个外键作为中间表的联合主键,也可以新建id字段作为中间表的主键 java如何处理、描述三种关联关系? // 一对一 class A{ B b; class B{ A a; // 一对多,A是一,B是多 class A{ List<B> b; class B{ A a; // 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射? <select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<resultMap> 用于自定义结果集映射
<!-- resultType不能和resultMap同时使用,fetchSize指定返回的记录数,未指定时默认返回所有满足要求的记录 --> <select id="" parameterType="" resultMap="userMap" fetchSize="10"> </select> <!-- type指定po --> <resultMap id="userMap" type="user"> <!-- 默认映射规则是同名映射,我们只需指定名称不一致的字段,主键字段用id指定,普通字段用result指定 --> <id property="id" column="user_id"/> <result property="username" column="user_name"/> </resultMap> <!-- resultMap属性指定要使用的<resultMap> --> <select id="" parameterType="" resultMap="userMap"> </select> 此外,<resultMap>还有2个常用子元素<association>、<collection>,用于关联映射。 关系数据库中,多表之间有三种关联关系 一对一:一张身份证对应一个人 <=> 一个人也只对应一张身份证 一对多:一个用户可以有多个订单 <=> 这多个订单都属于同一个用户 多对多:一个订单可以包含多种商品 <=> 一种商品可以属于多个订单 设计数据库时如何处理三种关联关系? 一对一:在一方引入另一方的主键作为外键 一对多:在多的一方,引入一的一方的主键作为外键 多对多:新建一张中间表,引入2张表的主键作为外键,可以使用这2个外键作为中间表的联合主键,也可以新建id字段作为中间表的主键 java如何处理、描述三种关联关系? // 一对一 class A{ B b; class B{ A a; // 一对多,A是一,B是多 class A{ List<B> b; class B{ A a; // 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射? <select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<!-- resultType不能和resultMap同时使用,fetchSize指定返回的记录数,未指定时默认返回所有满足要求的记录 --> <select id="" parameterType="" resultMap="userMap" fetchSize="10"> </select> <!-- type指定po --> <resultMap id="userMap" type="user"> <!-- 默认映射规则是同名映射,我们只需指定名称不一致的字段,主键字段用id指定,普通字段用result指定 --> <id property="id" column="user_id"/> <result property="username" column="user_name"/> </resultMap> <!-- resultMap属性指定要使用的<resultMap> --> <select id="" parameterType="" resultMap="userMap"> </select> 此外,<resultMap>还有2个常用子元素<association>、<collection>,用于关联映射。 关系数据库中,多表之间有三种关联关系
此外,<resultMap>还有2个常用子元素<association>、<collection>,用于关联映射。
关系数据库中,多表之间有三种关联关系
一对一:一张身份证对应一个人 <=> 一个人也只对应一张身份证 一对多:一个用户可以有多个订单 <=> 这多个订单都属于同一个用户 多对多:一个订单可以包含多种商品 <=> 一种商品可以属于多个订单 设计数据库时如何处理三种关联关系? 一对一:在一方引入另一方的主键作为外键 一对多:在多的一方,引入一的一方的主键作为外键 多对多:新建一张中间表,引入2张表的主键作为外键,可以使用这2个外键作为中间表的联合主键,也可以新建id字段作为中间表的主键 java如何处理、描述三种关联关系? // 一对一 class A{ B b; class B{ A a; // 一对多,A是一,B是多 class A{ List<B> b; class B{ A a; // 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射? <select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
设计数据库时如何处理三种关联关系?
java如何处理、描述三种关联关系?
// 一对一 class A{ B b; class B{ A a; // 一对多,A是一,B是多 class A{ List<B> b; class B{ A a; // 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射? <select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
// 一对一 class A{ B b; class B{ A a;
// 一对多,A是一,B是多 class A{ List<B> b; class B{ A a; // 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射? <select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
// 一对多,A是一,B是多 class A{ List<B> b; class B{ A a;
// 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射? <select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
// 多对多 class A{ List<B> b; class B{ List<A> a; 如果多表查询返回的结果集中包含另一方的字段,如何映射?
如果多表查询返回的结果集中包含另一方的字段,如何映射?
<select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap> 动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<select id="findStudentById" parameterType="integer" resultMap="studentWithScoreMap"> <!-- 一对一、一对多都是根据关联外键来查2张表,多对多是根据中间表来查2张表、是3表联查 --> SELECT st.*,sc.* FROM tb_student st ,tb_score sc WHERE st.id=#{id} AND st.id=sc.student_id </select> <resultMap id="studentWithScoreMap" type="student"> <!-- 如果其它字段名称不一致,手动映射 --> <id property="" column=""/> <result property="" column=""/> <!-- 一对一用association,javaType指定成员变量类型 --> <association property="score" javaType="score" /> <!-- 如果名称不匹配,手动指定映射 --> <association property="score" javaType="score"> <id property="" column="" /> <result property="" column=""/> </association> <!-- 一对多、多对多用collection。注意是ofType,指定List元素类型 --> <collection property="" ofType="" /> <!-- 如果名称不匹配,手动指定映射 --> <collection property="" ofType=""> <id property="" column="" /> <result property="" column=""/> </collection> </resultMap>
动态sql 动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。 <where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填 <select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
动态sql是mybatis的强大特性之一,可以实现sql语句的动态组装。
<where>、<if> where:如果有内容就添加关键字WHERE if:如果条件为true就添加内容 常用于多条件查询,一个输入框指定一个条件,有的输入框可能没填
<select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句 <select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<select id="findUser" resultType="user"> SELECT * FROM tb_user <where> <if test="name!=null and name!=''"> name=#{name} <if test="gender!=null and gender!=''"> AND tel=#{gender} </where> </select> <choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句
<choose>、<when>、<otherwise> 相当于switch语句,<when>相当于case,<otherwise>相当于default,条件为true就加上对应的sql语句
<select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。 <select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<select id="findStudent" resultType="student"> SELECT * FROM tb_student WHERE dep=#{dep} <choose> <when test="englishLevel=='cet4'"> AND english_level='cet4' </when> <when test="englishLevel=='cet6'"> AND english_level='cet6' </when> <otherwise> AND english_level<>'cet4' AND english_level<>'cet6' </otherwise> </choose> </select> <set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。 <set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。
<set> 传入pojo类型,更新多个字段,但对象的部分字段可能没有值,不能用jvm赋的初值null、0去覆盖表中原来的值,需要先判断。
<set>用于更新操作,可更新一个对象。如果有内容,会自动加关键字SET,并自动去除内容中多余的逗号。
<select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<select id="updateUser" parameterType="user"> UPDATE tb_user <if test="name!=null and name!=''"> <!-- 如果后面的内容都为false,会自动去掉这个逗号 --> name=#{name}, <if test="age!=0"> age=#{age}, <if test="tel!=null and tel!=''"> tel=#{tel}, </set> where id=#{id} </select> update语句至少要有一个字段更新,不然会报:语法错误。 <if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。 <foreach> <foreach>可迭代集合,常用于批量操作 collection 指定要迭代的集合名称,需要和形参名保持一致 index 当前迭代元素的下标,从0开始 item 当前迭代到的元素 open 拼接这整段字符串时以什么开头 close 拼接这整段字符串时以什么结尾 separator 分隔符 collection必需,其余均可选。 1、使用<foreach>迭代基本类型的list
update语句至少要有一个字段更新,不然会报:语法错误。
<if>不是必需的。<if>可以单独用,不一定搭配<where>、<set>。
<foreach>
<foreach>可迭代集合,常用于批量操作
collection必需,其余均可选。
1、使用<foreach>迭代基本类型的list
Integer batchAddMsgLog(Integer task_id, List<String> telList); <insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
Integer batchAddMsgLog(Integer task_id, List<String> telList);
<insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<insert id="batchAddMsgLog"> insert into tb_msg_log (task_id, user_tel) values <foreach collection="telList" item="tel" separator=","> (#{task_id}, #{tel}) </foreach> </insert> 2、使用<foreach>迭代实体类的list
2、使用<foreach>迭代实体类的list
Integer addAllTemplate(List<Template> templateList); <insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
Integer addAllTemplate(List<Template> templateList);
<insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。 <!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<insert id="addAllTemplate"> insert into tb_template values <foreach collection="templateList" item="template" separator=","> (#{template.template_id}, #{template.title}, #{template.content}, #{template.example}) </foreach> </insert> ${ }可拼接字符串,但有sql注入的风险。 使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。 <bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。
${ }可拼接字符串,但有sql注入的风险。
使用数据库自带的方式拼接字符串没有sql注入的风险,mysql可以使用concat()拼接字符串,oracle可以使用 || 拼接字符串,但都只能针对特定的数据库使用,不利于项目移植。
<bind>可拼接字符串,没有sql注入的风险,所有数据库都可用。
<!-- 模糊查询 --> <select id="queryUser" parameterType="string" resultType="user"> <bind name="pattern_name" value="'%'+name+'%'"/> SELECT * FROM user_tb WHERE name LIKE #{pattern_name} </select> 1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性 相等判断:==,!= 逻辑与:只能用and,不能用&& 逻辑或:or、||均可 其它常见操作 传入实体类执行insert操作,插入后实体类主键属性值自增 有2种情况 1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增 <!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
1、在属性值中使用变量 时直接用即可,不用放在#{ }中;在标签体中使用变量时,要放在#{ }中。 2、test属性
有2种情况
1、使用的是支持主键自增的数据库,比如mysql、sql server,设计表时勾选主键自增
<!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值 <insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<!-- keyProperty指定实体类的主键字段,useGeneratedKeys指定使用主键自增 --> <insert id="addUser" parameterType="user" keyProperty="id" useGeneratedKeys="true"> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> insert语句中不需要设置主键字段的值。 2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。 需要增加一个select来查询主键字段应取的值
insert语句中不需要设置主键字段的值。
2、使用的是不支持主键自增的数据库,比如oracle;或者使用的是支持主键自增的数据库,但设计表时没有勾选主键自增。
需要增加一个select来查询主键字段应取的值
<insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写 <insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<insert id="addUser" parameterType="user"> <!--keyProperty指定实体类的主键字段,order指定select是在insert之前还是之后执行 --> <selectKey keyProperty="id" resultType="integer" order="BEFORE"> select if(max(id) is null, 1, max(id)+1) from tb_user </selectKey> insert into tb_user(id,nickname,tel) values (#{id},#{nickname},#{tel}) </insert> before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。 情况1也可以这样写
before的执行过程: 执行select获取主键字段应该的值,自动调用setter方法设置实体类主键字段的值,执行insert插入记录。insert中需要设置主键字段的值。
情况1也可以这样写
<insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。 #url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<insert id="addUser" parameterType="user"> <!--使用after--> <selectKey keyProperty="id" resultType="integer" order="AFTER"> select id from tb_user where nickname=#{nickname} and tel=#{tel} </selectKey> insert into tb_user(nickname,tel) values (#{nickname},#{tel}) </insert> after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。 before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。 before、after的sql语句写法是不同的。
after的执行过程: 先执行insert插入记录,再执行select获取主键字段的值,自动调用setter方法设置实体类中主键字段的值。insert中不需要设置主键字段的值。
before可以检查返回的受影响的记录数是否为1来判断插入是否成功(期间是否有新的insert,导致主键字段的值变化),不用加乐观锁;而after的select不能直接选取max(id),因为insert之后、select之前可能有其他的insert导致max(id)不是当前记录的id。
before、after的sql语句写法是不同的。
#url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC <!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
#url中不写具体的具体库 jdbc:mysql://127.0.0.1:3306/?serverTimezone=UTC
<!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete> 判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
<!-- xml映射文件中,写成`库名`.`表名`的形式,库名、表名都要加反引号,不然会报错:java.sql.SQLException: No database selected --> <delete id="deleteAll"> delete from `库名`.`表名`; </delete>
判断增删改的操作结果 在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上 useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
在mapper接口中把增删改的返回值类型声明为int,并在数据库连接的url中加上
useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。 同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
useAffectedRows=true 如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。
如果不加,默认返回的是匹配的记录数(执行成功+失败的记录数),而我们需要的是受影响的记录数(执行成功的记录数)。
同时执行多条sql语句 在数据库连接的url中加上 #允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
在数据库连接的url中加上
#允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。 BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
#允许同时执行多条sql allowMultiQueries=true 这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。
这样在mybatis的sql语句中就可以使用分号拼接多条sql,一般用于批量操作。
BindingException: Invalid bound statement (not found) 检查以下方面 1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致 2、application.properties中mybatis的配置是否正确 3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml bad SQL grammar [] 原因:参数列表、数组类型,但传入的是一个空列表、空数组。 不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。 eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。 eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。 多表关联查询时结果集不映射 原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名 解决:给列起别名,使用别名进行映射 Column ‘xxx’ in field list is ambiguous java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous 原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。 解决:select xxx.xxx , 指定表名
检查以下方面
1、xml映射文件的namespace和接口名是否一致,namespace要是接口名的全路径,id和接口中的函数名是否一致,参数类型、返回值类型是否一致
2、application.properties中mybatis的配置是否正确
3、xml映射文件是否忘写了后缀.xml 第二个才是对的,新建映射文件时很容易忘记加后缀.xml
原因:参数列表、数组类型,但传入的是一个空列表、空数组。
不管是调用dao层的方法,还是调用其它方法,在调用之前,该检查判断的要先检查判断。
eg. subList(start, end)提取list的一部分,调用方法之前要先检查start、end是不是在合法的区间上,有没有越界。
eg. dao层传入list进行批量操作,调用方法之前要先检查list是否为空。
原因:select以 表名.列名 的形式选取字段,结果集映射时识别不了 表名.列名 形式的列名
解决:给列起别名,使用别名进行映射
java.sql.SQLIntegrityConstraintViolationException: Column ‘prize_name’ in field list is ambiguous
原因:多表关联查询,select xxx 直接选取该字段,但该字段在查询的多个表中存在,未指定使用哪张表的。
解决:select xxx.xxx , 指定表名