mybatis(三) XML映射器之select、update、delete、insert标签

3、XML映射器

  • 建表:user(用户表)、shoppingcart(购物车表)、goods(商品表)

DROP TABLE IF EXISTS `goods`;
CREATE TABLE `goods` (
  `good_id` int(11) NOT NULL AUTO_INCREMENT,
  `good_name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`good_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ----------------------------
-- Records of goods
-- ----------------------------
BEGIN;
INSERT INTO `goods` VALUES (1, '上衣');
INSERT INTO `goods` VALUES (2, '裤子');
INSERT INTO `goods` VALUES (3, '鞋子');
COMMIT;

-- ----------------------------
-- Table structure for shoopingcart
-- ----------------------------
DROP TABLE IF EXISTS `shoopingcart`;
CREATE TABLE `shoopingcart` (
  `cart_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `good_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`cart_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ----------------------------
-- Records of shoopingcart
-- ----------------------------
BEGIN;
INSERT INTO `shoopingcart` VALUES (1, 1, 1);
INSERT INTO `shoopingcart` VALUES (2, 1, 2);
INSERT INTO `shoopingcart` VALUES (3, 2, 1);
INSERT INTO `shoopingcart` VALUES (4, 3, 2);
INSERT INTO `shoopingcart` VALUES (5, 3, 3);
COMMIT;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) DEFAULT NULL,
  `pwd` varchar(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ----------------------------
-- Records of user
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES (1, 'user1', 'pwd1');
INSERT INTO `user` VALUES (2, 'user2', 'pwd2');
INSERT INTO `user` VALUES (3, 'user3', 'pwd3');
COMMIT;

DROP TABLE IF EXISTS `wallet`;
CREATE TABLE `wallet` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `balance` decimal(10,2) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ----------------------------
-- Records of wallet
-- ----------------------------
BEGIN;
INSERT INTO `wallet` VALUES (1, 1, 100.00);
INSERT INTO `wallet` VALUES (2, 2, 200.00);
INSERT INTO `wallet` VALUES (2, 3, 300.00);
COMMIT;
  • 实体类:
package com.szm.pojo;

@Data
public class Good {
    private int goodId;
    private String goodName;
}

package com.szm.pojo;

import lombok.Data;

@Data
public class ShoppingCart {

    private int cartId;
    private int userId;
    private int goodId;

}

package com.szm.pojo;

import lombok.Data;


@Data
public class User {
    private int userId;
    private String userName;
    private String userPwd;

}

package com.szm.pojo;

import lombok.Data;

import java.math.BigDecimal;

@Data
public class Wallet {
    private int id;
    private BigDecimal balance;
}

3.1、select

3.1.1、单表查询
  • dao接口
package com.szm.mapper;

import com.szm.pojo.User;

import java.util.List;


public interface UserMapper {

    List<User> selectUserByName(String name);

}

  • 映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.szm.mapper.UserMapper">
    <select id="selectUserByName" resultType="user">
        select id as userId,name as userName,pwd as userPwd from user where name =#{name};
    </select>
</mapper>
3.1.2、select标签属性列表
属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap(废弃)用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。
resultType期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
resultMap对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
fetchSize这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetTypeFORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。
resultOrdered这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false
3.1.3、parameterType属性

parameterType用来声明这条语句所需要的入参,一般不需要在xml进行显式的定义,mybatis会自动进行处理,下面列举参数类型的几种情况

  • 传入多个基本类型的参数,有两种方法:
    • 首先定义一个dao接口
package com.szm.mapper;

import com.szm.pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;


public interface UserMapper {
    //传入两个参数 
    List<User> selectUser( String userName, String pwd);

}


  • 第一种在映射文件中使用**#{arg0},#{arg1}或#{param1}, #{param2}**来占位,根据dao方法定义的参数顺序就可以使用了。**mybatis3.4.2之前可以直接用#{0}、#{1},之后就只能用前面说的两种 **
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.szm.mapper.UserMapper">
    <select id="selectUser" resultType="user" >
        select id as userId,name as userName,pwd as userPwd from user where name =#{arg0} and pwd = #{arg1};
    </select>

</mapper>
  • 第二种需要使用**@Param(“参数别名”)指定参数名称**,映射文件就可以直接使用这个名称
package com.szm.mapper;

import com.szm.pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;


public interface UserMapper {

    List<User> selectUser(@Param("name") String userName,@Param("p") String pwd);

}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.szm.mapper.UserMapper">
    <select id="selectUserByName" resultType="user" >
        select id as userId,name as userName,pwd as userPwd from user where name =#{name} and pwd = #{p};
    </select>
</mapper>
  • 如果传入参数包含对象,需要使用@Param注解来指定参数的名称
package com.szm.mapper;

import com.szm.pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserMapper {

    List<User> selectUserByName(@Param("queryUser")User user);

}

映射文件使用**#{名称.参数名称}**的方式进行占位

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.szm.mapper.UserMapper">
    <select id="selectUserByName" resultType="user" >
        select id as userId,name as userName,pwd as userPwd from user where name =#{queryUser.name} and pwd = #{queryUser.pwd};
    </select>
</mapper>
3.1.4、resultType
  • resultType官方解释:

期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个

  • 在使用select标签做查询时,使用resultType定义返回类型,如果查询返回的是mybatis内置的类型的话,直接使用java内置的类型别名或使用类型全限定路径(内置类型详见我的博客《mybatis(二)xml配置方式详细说明》)。

  • 如果返回结果为javaBean对象,mybatis根据查询结果的列名和javaBean属性名称进行对应,所以javaBean对象的属性名称要和sql结果列名完全一致

  • 需要注意的是如果返回的是集合对象,resultType只需定义集合的泛型类型就行了

    下面写几个例子来说明:

    • 如果返回的是mybatis内置类型:
package com.szm.mapper;

import java.util.List;
import java.util.Map;

public interface UserMapper {
    // 返回map类型
    List<Map<String,Object>> selectUser(String userName,String pwd);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.szm.mapper.UserMapper">
    <!--内置类型直接使用内置的别名,Map的内置别名为map -->
    <select id="selectUser" resultType="map" >
        select id as userId,name as userName,pwd as userPwd from user where name =#{arg0} and pwd = #{arg1};
    </select>

</mapper> 
  • 如果返回的是mybatis非内置类型而是我们自定义的javaBean呢?

  • 如果是普通的javabean对象,其中都是基础类型的话,在resultType中定义我们的自定义javaBean的类全限定路径或者定义一个typeAliases使用别名(别名配置详见《mybatis(二)xml配置方式详细说明》)这里就不在赘述了,那如果自定义javaBean对象又引用了另外一个javaBean或者定义了一个结构更复杂的属性呢,mybatis非自动处理吗?

  • 我们定义一个dto对象,对象中包含user的两个字段和一个集合对象cartGoods(购物车里所有的货物集合):

package com.szm.dto;

import com.szm.pojo.Good;
import lombok.Data;

import java.util.List;

@Data
public class UserInfo {

    private int userId;
    private String userName;
    List<Good> cartGoods;
}

package com.szm.mapper;

import com.szm.dto.UserInfo;

import java.util.List;
import java.util.Map;


public interface UserMapper {
    UserInfo selectUserInfo(int userId);
}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.szm.mapper.UserMapper">
    <select id="selectUserInfo" resultType="com.szm.dto.UserInfo">
       	select
        a.id as userId,
        a.name as userName,
        c.good_id as goodId,
        c.good_name as goodName
        from (select * from user where id = #{arg0}) a
        left join shoopingcart b on a.id = b.user_id
				left join goods c on b.good_id = c.good_id;
    </select>
</mapper>

我们期待mybatis能够自动帮我们把结果映射,结果可见:mybatis并没有那么智能,运行结果:

org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2

	at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:80)
	at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:87)
	at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:152)
	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:85)
	at com.sun.proxy.$Proxy6.selectUserInfo(Unknown Source)
	at Test1.test2(Test1.java:48)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

Process finished with exit code 255

  • 根据mybatis在resultType的官方文档:

MyBatis 会在幕后自动创建一个 ResultMap,再根据属性名来映射列到 JavaBean 的属性上

所以,resultType其实在本质是是一个resultMap,那对于复杂的情况,我们移步看resultMap吧!

3.1.5、resultMap
3.1.5.1、resultMap内部标签列表
  • 标签很多丰富好用的功能标签供我们使用

  • 标签列表:

标签名称用途
constructor用于在实例化类时,注入结果到构造方法中
id一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
result注入到字段或 JavaBean 属性的普通结果
association一个复杂类型的关联;许多结果将包装成这种类型, 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
collection一个复杂类型的集合
discriminator使用结果值来决定使用哪个 resultMap
3.1.5.2 constructor和discriminator标签
  • constructor和discriminator标签(其他标签下面的一对一,一对多会用到先不讲):

resultMap定义可以理解为就是在定义一个对象来接收sql的一条查询结果,那这个对象在进行初始化和属性注入的时候mybatis约定了一些标签方便我们做一些处理

  • constructor允许我们定义在初始化返回对象的时候,将当前结果集中当前记录的某些字段注入到到我们的构造方法中,也就是说如果我们定义了这个标签,mybatis就会在初始化对象时调用我们的有参构造方法

  • discriminator ,定义了这个这个标签的话,mybatis在注入结果集时,会根据配置对当前记录行的某些字段进行判断来决定当前行使用那个resultMap


    下面简单运用一下这两个标签

  • 新建一个UserInfo对象,新增一个有参构造,在构造方法中对extend属性进行赋值:

package com.szm.pojo;

import lombok.Data;

import java.util.UUID;


@Data
public class User {
    public User(){

    }
    public User(String extend){
        this.extend = extend+ UUID.randomUUID();

    }
    private int userId;
    private String userName;
    private String userPwd;
    private String extend;
}

dao层接口

package com.szm.mapper;



import com.szm.dto.UserCart;
import com.szm.dto.UserInfo;
import com.szm.pojo.User;

import java.util.List;


public interface UserMapper {

    List<User> select();
   
}

xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--baseUserMap2对查询结果的name和pwd映射进行了互换 -->
<mapper namespace="com.szm.mapper.UserMapper">
    <resultMap id="baseUserMap2" type="user">
        <id property="userId" column="id"/>
        <result property="userName" column="pwd"/>
        <result property="userPwd" column="name"/>
    </resultMap>

    <resultMap id="baseUserMap" type="user">
      <!-- 注入name列到我们的构造器中-->
        <constructor>
            <idArg column="name" javaType="string"/>
        </constructor>
        <id property="userId" column="id"/>
        <result property="userName" column="name"/>
        <result property="userPwd" column="pwd"/>
      <!--如果当前列的id等于1,则使用baseUserMap2作为当前列的resultMap, -->
        <discriminator javaType="int" column="id">
            <case value="1" resultMap="baseUserMap2"></case>
        </discriminator>
    </resultMap>

    <select id="select" resultMap="baseUserMap">
    select * from user;
    </select>
</mapper>
  • 期望的结果是 id=1的记录 name 和pwd字段互换,其他记录的extend值为name+uuid,运行结果如下,符合预期:
User(userId=1, userName=pwd1, userPwd=user1, extend=null)
User(userId=2, userName=user2, userPwd=pwd2, extend=user2f1ae73cd-5ced-4778-8f97-ada7c690cdaf)
User(userId=3, userName=user3, userPwd=pwd3, extend=user3e012f90f-75aa-4294-9727-cf8abae884c3)
3.1.5.2、association标签一对一
  • 首先定义一个结果对象
package com.szm.dto;

import com.szm.pojo.Wallet;
import lombok.Data;


@Data
public class UserInfo {

    private int userId;
    private String userName;
    private String userPwd;
    private Wallet wallet;

}

  • 定义dao层接口
package com.szm.mapper;



import com.szm.dto.UserInfo;
import com.szm.pojo.User;

import java.util.List;
import java.util.Map;


public interface UserMapper {
    
    List<UserInfo> selectUser();
}

  • 可以看到sql查询结果列名和javaBean对象属性名不一样,在书写sql的时如果不想使用as来取列别名的话,可以使用resultMap对查询结果配置映射。
  • 如果javaBean中存在其他javaBean对象属性需要映射的话,可以使用association标签定义,配置如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.szm.mapper.UserMapper">

    <resultMap id="userMap" type="userInfo">
        <id property="userId" column="id"/>
        <result property="userName" column="name"/>
        <result property="userPwd" column="pwd"/>
        <association property="wallet" >
            <result property="balance" column="balance"></result>
        </association>
    </resultMap>

    <select id="selectUser" resultMap="userMap">
        select a.id,a.name,a.pwd,b.balance from user a
        left join wallet b on a.id = b.user_id
    </select>

</mapper>
  • 查询结果
UserInfo(userId=1, userName=user1, userPwd=pwd1, wallet=Wallet(id=0, balance=100.00))
UserInfo(userId=2, userName=user2, userPwd=pwd2, wallet=Wallet(id=0, balance=200.00))
UserInfo(userId=3, userName=user3, userPwd=pwd3, wallet=Wallet(id=0, balance=300.00))

Process finished with exit code 0
  • 也可以将wallet拎出来定义成一个resultMap,方便复用
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.szm.mapper.UserMapper">

    <resultMap id="wallet" type="com.szm.pojo.Wallet">
        <result property="balance" column="balance"></result>
    </resultMap>
    <resultMap id="userMap" type="userInfo">
        <id property="userId" column="id"/>
        <result property="userName" column="name"/>
        <result property="userPwd" column="pwd"/>
        <association property="wallet" resultMap="wallet" />
    </resultMap>

    <select id="selectUser" resultMap="userMap">
        select a.id,a.name,a.pwd,b.balance from user a
        left join wallet b on a.id = b.user_id
  </select>

</mapper>
3.1.5.3、collection标签一对多
  • 如果查询结果对象包含的是一个集合类型的javaBean属性呢?例如:
package com.szm.dto;

import com.szm.pojo.Good;

import java.util.List;

public class UserCart {
    private int userId;
    private String userName;
    private List<Good> goods;
}

  • dao接口
package com.szm.mapper;



import com.szm.dto.UserCart;
import com.szm.dto.UserInfo;

import java.util.List;


public interface UserMapper {
   
    List<UserCart> selectUserCart();
}

此时配合result的collection就可以轻松解决:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.szm.mapper.UserMapper">

    

    <!-- 一对多-->
    <resultMap id="cartGood" type="com.szm.pojo.Good">
        <result property="goodName" column="good_name"/>
    </resultMap>

    <resultMap id="userCartMap" type="com.szm.dto.UserCart">
        <id property="userId" column="id"></id>
        <result property="userName" column="name"></result>
        <collection property="goods" resultMap="cartGood"/>
    </resultMap>

    <select id="selectUserCart" resultMap="userCartMap">
       select
        a.id ,
        a.name ,
        c.good_id ,
        c.good_name
        from user a
        left join shoopingcart b on a.id = b.user_id
		left join goods c on b.good_id = c.good_id;
    </select>


</mapper>
  • 运行结果:
UserCart(userId=1, userName=user1, goods=[Good(goodId=0, goodName=上衣), Good(goodId=0, goodName=裤子)])
UserCart(userId=2, userName=user2, goods=[Good(goodId=0, goodName=上衣)])
UserCart(userId=3, userName=user3, goods=[Good(goodId=0, goodName=裤子), Good(goodId=0, goodName=鞋子)])

Process finished with exit code 0
3.1.5.4、resultMap继承
  • resultMap也可以像java中的extends一样通过继承获取父类的定义
<resultMap id="publicUserMap" type="user">
        <id property="userId" column="id"/>
        <result property="userName" column="name"/>
        <result property="userPwd" column="pwd"/>
    </resultMap>
    <!--extends定义之后 baseUserMap继承了publicUserMap的定义 -->
    <resultMap id="baseUserMap" type="user" extends="publicUserMap">
        <constructor>
            <idArg column="name" javaType="string"/>
        </constructor>
        <discriminator javaType="int" column="id">
            <case value="1" resultMap="baseUserMap2"></case>
        </discriminator>
    </resultMap>
  • 运行结果和上方的一致
3.1.6、sql和include标签

sql标签可以定义一些可重用的 SQL 代码片段,以便在其它语句中使用,参数也可以在运行中确定下来,基于这个特性可以结合select做一些更灵活的配置

  • include标签可以应用sql标签定义的内容

具体如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.szm.mapper.UserMapper">
		
    <sql id="userSql" >${userTable}.id,${userTable}.name,${userTable}.pwd</sql>


    <resultMap id="wallet" type="com.szm.pojo.Wallet">
        <result property="balance" column="balance"></result>
    </resultMap>
    <resultMap id="userMap" type="userInfo">
        <id property="userId" column="id"/>
        <result property="userName" column="name"/>
        <result property="userPwd" column="pwd"/>
        <association property="wallet" resultMap="wallet" />
    </resultMap>

    <select id="selectUser" resultMap="userMap">
        select
        <include refid="userSql"><property name="userTable" value="a"/></include>,
        b.balance
        from user a
        left join wallet b on a.id = b.user_id
    </select>


    <resultMap id="cartGood" type="com.szm.pojo.Good">
        <id property="goodId" column="good_id" />
        <result property="goodName" column="good_name"/>
    </resultMap>

    <resultMap id="userCartMap" type="com.szm.dto.UserCart">
        <id property="userId" column="id"></id>
        <result property="userName" column="name"></result>
        <result property="goodId" column="good_id"></result>
        <result property="goodName" column="good_name"></result>

    </resultMap>

    <select id="selectUserCart" resultType="com.szm.dto.UserInfo">
       select
        a.id ,
        a.name ,
        c.good_id ,
        c.good_name
        from (select * from user where id = #{arg0}) a
        left join shoopingcart b on a.id = b.user_id
		left join goods c on b.good_id = c.good_id;
    </select>


</mapper>


3.2、insert, update 和 delete

数据变更语句 insert,update 和 delete 的实现非常接近:

  • Insert, Update, Delete 元素的属性
属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap(废弃)用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
keyProperty(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。

例子:

 		<insert id="insertUser">
        insert into user(id,name,pwd)
        values(#{id},#{name},#{pwd});
    </insert>
    
    <update id="updateUser">
        update user set name= #{name} where id = #{id}
    </update>
    
    <delete id="deleteUser">
        delete from user where id = #{id}
    </delete>
  • 这三个标签比较简单,着重讲一下几个扩展:
3.2.1、插入自动生成主键

如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server,那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置为目标属性就 OK 了,这样可以省略插入主键id,这里其实如果你设置了主键自动增长,是不用设置这两个属性也可以插入成功的,但是如果你要获得返回的主键id的话就必须要设置,keyProperty为传入的javaBean的属性名称,注意这里的返回其实是传入的对象本身,mybatis在底层对传入的对象做了一个setId的操作再返回了

	<insert id="insertUser" useGeneratedKeys="true"  keyProperty="userId">
        insert into user(name,pwd)
        values(#{user.name},#{user.pwd});
    </insert>
3.2.2、批量插入

如果你的数据库还支持多行插入, 你也可以传入一个数组或集合,并返回自动生成的主键,注意这里的返回其实是传入的对象本身,mybatis在底层对传入的对象做了一个setId的操作再返回了

User新增一个构造方法

package com.szm.pojo;

import lombok.Data;

import java.util.UUID;

@Data
public class User {
    public User() {

    }

    public User(String extend) {
        this.extend = extend + UUID.randomUUID();

    }

    public User(String name, String pwd) {
        this.userName = name;
        this.userPwd = pwd;
    }

    private int userId;
    private String userName;
    private String userPwd;
    private String extend;
}

dao:


package com.szm.mapper;




import com.szm.pojo.User;

import java.util.List;

public interface UserMapper {

	// 注意这里返回的其实是传入的对象本身,mybatis在底层对传入的对象做了一个setId的操作再返回了 
   void batchInsertUser(@Param("users") List<User> users);
}

<insert id="batchInsertUser" useGeneratedKeys="true" keyProperty="userId" >
        insert into user (name, pwd) values
        <foreach item="user" collection="users" separator=",">
            (#{user.userName}, #{user.userPwd})
        </foreach>
</insert>

测试类

 @Test
    public void test4() throws IOException {
        String resource = "mybatis.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = new ArrayList<>();
        users.add(new User("user4","pwd4"));
        users.add(new User("user4","pwd5"));
         mapper.batchInsertUser(users);
        users.stream().forEach((e)->{
            System.out.println(e);
        });
        sqlSession.close();
    }

运行结果:

User(userId=8, userName=user4, userPwd=pwd4, extend=null)
User(userId=9, userName=user4, userPwd=pwd5, extend=null)

3.2.3、 selectKey
  • 在mybatis官方文档中,这个用来作为在数据库不支持自动生成主键的一种替代方案,本质上就是定义一段查询结果为一行一列的查询sql,mybatis将会根据你定义order在查询前/后执行这段sql,将查询结果注入到你传入的对象对应的属性中。

    官方示例:

    <insert id="insertAuthor">
      <selectKey keyProperty="id" resultType="int" order="BEFORE">
        select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
      </selectKey>
      insert into Author
        (id, username, password, email,bio, favourite_section)
      values
        (#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
    </insert>
    
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MyBatis 中,您可以使用自定义映射器(Mapper)来为任何 SQL 语句提供自定义实现。自定义映射器是一种与 MyBatis 映射器相似的接口,它将 SQL 语句与 Java 方法连接起来。在 MyBatis 中,自定义映射器可以通过 XML 方式或注解方式来定义。 以下是如何创建一个自定义映射器的步骤: 1. 创建一个 Java 接口,该接口将作为自定义映射器的入口。在该接口中,您需要定义与 SQL 语句相对应的方法。例如: ``` public interface UserMapper { @Select("SELECT * FROM Users WHERE id = #{id}") User getUserById(int id); @Insert("INSERT INTO Users (id, name, age) VALUES (#{id}, #{name}, #{age})") void insertUser(User user); @Update("UPDATE Users SET name = #{name}, age = #{age} WHERE id = #{id}") void updateUser(User user); @Delete("DELETE FROM Users WHERE id = #{id}") void deleteUserById(int id); } ``` 在这个示例中,UserMapper 接口定义了四个方法,分别对应 SQL 语句中的 SELECTINSERTUPDATEDELETE 操作。每个方法都使用注解来指定 SQL 语句。注意,方法参数和 SQL 语句中的占位符必须匹配。 2. 创建一个 XML 映射器文件(可选)。如果您不想使用注解来指定 SQL 语句,可以创建一个 XML 映射器文件来定义 SQL 语句和参数映射。例如: ``` <mapper namespace="com.example.UserMapper"> <select id="getUserById" resultType="com.example.User"> SELECT * FROM Users WHERE id = #{id} </select> <insert id="insertUser"> INSERT INTO Users (id, name, age) VALUES (#{id}, #{name}, #{age}) </insert> <update id="updateUser"> UPDATE Users SET name = #{name}, age = #{age} WHERE id = #{id} </update> <delete id="deleteUserById"> DELETE FROM Users WHERE id = #{id} </delete> </mapper> ``` 在这个示例中,XML 映射器文件与注解方式类似,定义了 getUserById、insertUser、updateUser 和 deleteUserById 方法对应的 SQL 语句。 3. 在 MyBatis 配置文件中注册自定义映射器。例如: ``` <configuration> <mappers> <mapper class="com.example.UserMapper"/> </mappers> </configuration> ``` 在这个示例中,UserMapper 自定义映射器被注册到 MyBatis 配置中。 4. 使用自定义映射器。例如: ``` SqlSessionFactory factory = ...; SqlSession session = factory.openSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.getUserById(1); mapper.insertUser(new User(2, "Alice", 20)); mapper.updateUser(new User(2, "Alice", 21)); mapper.deleteUserById(2); session.commit(); session.close(); ``` 在这个示例中,我们使用 SqlSession.getMapper 方法获取 UserMapper 接口的实现,然后调用接口中定义的方法来执行 SQL 语句。注意,所有 SQL 操作必须在事务中执行,并在完成后调用 SqlSession.commit 方法提交事务。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值