Java 程序中使用 Map实现动态传参

在 Java 编程中,我们常常需要将数据传递给 SQL 查询,特别是在动态生成 SQL 语句时,通常会用到 Map 这种集合类。Map 可以将多个键值对传递给 SQL 语句的占位符,完成动态参数绑定的功能。我们下面详细讲解图中的知识点。

1. Map 的基本用法

Map 是 Java 集合框架中的一种数据结构,它以键值对 (key-value) 的形式存储数据。Map 中的键唯一且不能重复,而值可以重复。常用的实现类包括 HashMap、TreeMap 等。

示例代码:

Map<String, Object> map = new HashMap<>();
map.put("k1", "1111");
map.put("k2", "比亚迪汉");
map.put("k3", 10.0);
map.put("k4", "2020-11-11");
map.put("k5", "电车");

这里我们创建了一个 HashMap,并通过 put 方法将一些数据存入 Map。键是 String 类型,值是 Object 类型,意味着可以存放不同类型的数据(如字符串、数字、日期等)。

2. 占位符 #{} 的使用

在执行 SQL 语句时,为了防止 SQL 注入和简化代码,我们经常使用占位符将 SQL 参数动态地传入。( ? 是 JDBC 中的占位符,用于预编译 SQL 语句中的参数。在使用 JDBC 时,SQL 语句通常是预编译的,? 用于表示一个位置,在执行时由具体的参数替换。)占位符 #{} 这一语法主要用于一些持久化框架,如 MyBatis。

SQL 语句示例:

insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) 
values(null, #{k1}, #{k2}, #{k3}, #{k4}, #{k5});

在这个 SQL 语句中,#{k1}、#{k2} 等占位符将会在运行时被替换为 Map 中对应的值。框架会根据占位符的键来查找 Map 中的值并动态替换。如果键不存在,则返回 null。这个语句通常配置在通常用于 MyBatis 的 SQL 映射的XML文件中

注意:

  • 如果 Map 中没有提供某个占位符对应的值,查询执行时可能会插入 null,这可能导致意外的行为。
  • 使用占位符 #{} 可以防止 SQL 注入,因为框架在执行时会自动进行预处理。

3. Map 键名与数据库字段名的对应关系

为了使代码更加易读、维护更加方便,我们通常建议将 Map 中的键名与数据库字段名保持一致。这样一来,不仅能够直观地映射参数,还能避免因键名不统一而导致的问题。

map.put("carNum", "1111");
map.put("brand", "比亚迪汉2");
map.put("guidePrice", 10.0);
map.put("produceTime", "2020-11-11");
map.put("carType", "电车");

在这个例子中,我们将 Map 的键名改为 carNum、brand 等,和数据库表的列名一致。这样一来,代码就更容易理解和维护。

SQL 语句:

insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) 
values(null, #{carNum}, #{brand}, #{guidePrice}, #{produceTime}, #{carType});

POJO 类实现动态传参

1. POJO 类与 SQL 占位符传递

POJO(Plain Old Java Object) 是指普通的 Java 对象,通常用作数据传输对象。在 Java 应用中,我们可以通过 POJO 类的属性来传递 SQL 语句中的参数。

Car car = new Car(null, "3333", "比亚迪秦", 30.0, "2020-11-11", "新能源");

在这个例子中,Car 是一个 POJO 类,包含一些车辆信息的属性。通过实例化这个类,我们可以将具体的值传入到 SQL 语句中的占位符。

2. 占位符 #{} 的使用

在 MyBatis 等持久化框架中,使用占位符 #{} 来表示将 POJO 的属性值注入到 SQL 语句中。大括号 {} 里面填写的内容是 POJO 类的属性名(严格意义上来说:如果使用P0J0对象传递值的话,这个{}里面写的是get方法的方法名去掉get,然后将剩下的单词首字母小写,然后放进去。)

SQL 语句示例:

insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) 
values(null, #{carNum}, #{brand}, #{guidePrice}, #{produceTime}, #{carType});

3. 占位符与属性对应的规则

MyBatis 会自动根据 POJO 类中的属性名来查找对应的 getter 方法。如果 POJO 类中没有提供对应的 getter 方法,则会报错。

错误示例:

There is no getter for property named 'xyz' in 'class com.powernode.mybatis.pojo.Car'

这是因为 MyBatis 尝试通过 getXyz() 方法来获取 xyz 属性的值,但 POJO 类中没有定义这个方法。为了避免这种错误,我们需要在 POJO 类中提供每个属性的 getter 和 setter 方法。

4. 如何解决没有 getter 方法的问题?

如果遇到没有对应 getter 方法的情况,可以通过在 POJO 类中手动添加相应的 getter 方法来解决。比如:

public String getXyz() {
    return xyz;
}

5. 占位符 #{} 的使用规则

通过这个错误示例,我们可以总结出占位符的使用规则:MyBatis 是通过反射机制来调用 getter 方法。也就是说,在 #{} 占位符中写的内容(如 #{carNum})并不直接对应 POJO 的属性名,而是对应属性的 getter 方法。

规则:

  • 占位符 #{} 中的内容是首字母小写形式。
  • 占位符内部自动调用属性的 getter 方法,因此命名应符合 Java 的命名规范。例如:
    • getUsername() 对应的占位符为 #{username}
    • getEmail() 对应的占位符为 #{email}

占位符 #{} 中的内容确实是与 POJO 类的属性相关,但 MyBatis 实际上是通过 调用 POJO 类的 getter 方法 来获取这些值的,而不是直接访问属性本身。因此,为了确保 MyBatis 能正确地映射 SQL 参数,POJO 类中的 getter 方法必须与占位符中的名字相匹配。 

删除DELETE操作:

 1. 删除操作的实现

int count = sqlSession.delete("deleteById", 59);

这行代码展示了通过 sqlSession.delete() 方法来执行删除操作,其中:

  • sqlSession:MyBatis中的会话对象,允许我们与数据库进行交互。
  • "deleteById":这是Mapper XML文件中定义的SQL语句的唯一标识符,用来指定删除操作。
  • 59:这是要传递给SQL语句的参数,即要删除的记录的ID

2. MyBatis Mapper 文件中的SQL语句 

<delete id="deleteById">
    delete from t_car where id = #{fdsfd}
</delete>

这是在MyBatis的Mapper XML文件中定义的删除操作,具体解释如下:

  • <delete id="deleteById">:这是定义一个删除操作的方法,id 是这个方法的唯一标识符,名称为 deleteById,与Java代码中调用的 deleteById 相对应。
  • delete from t_car where id = #{fdsfd}:这是一个标准的SQL删除语句,意思是从表 t_car 中删除 id 为 #{fdsfd} 的记录。
    • #{fdsfd}:这是MyBatis的参数占位符,表示将传递的参数(在Java代码中传递的 59)注入到这个位置。fdsfd 是参数名,可以根据需要定义。

3. 占位符的说明

注意:如果占位符只有一个,那么 #{} 的大括号里可以随意。但是最好见名知意。

这里说明了 #{} 是MyBatis的占位符,用于绑定参数。虽然在某些情况下可以随意使用参数名(如 fdsfd),但是为了代码的可读性和维护性,建议使用有意义的名称,比如 id。

例如,改为:

<delete id="deleteById">
    delete from t_car where id = #{id}
</delete>

这样更直观,容易理解。

4. 整体流程总结

  • 在Java代码中,我们通过 sqlSession.delete("deleteById", 59) 来调用Mapper文件中定义的 deleteById 删除方法,并传递 id = 59。
  • MyBatis会将 59 替换到 delete from t_car where id = #{id} 的SQL语句中,生成最终的SQL语句 delete from t_car where id = 59 并执行,最终删除 t_car 表中 id 为 59 的记录。

更新操作:

1. 更新操作的SQL语句

<update id="updateById">
    update t_car set
        car_num=#{carNum},
        brand=#{brand},
        guide_price=#{guidePrice},
        produce_time=#{produceTime},
        car_type=#{carType}
    where
        id = #{id}
</update>

这是在MyBatis的Mapper XML文件中定义的更新操作。解释如下:

  • <update id="updateById">:定义一个更新操作,id 是唯一标识符,这里使用 updateById,与Java代码中的方法名一致。
  • update t_car set ... where id = #{id}:这是SQL的更新语句,意图是将 t_car 表中的某条记录进行更新,具体是通过 id 来定位记录,然后对表中的多个字段进行更新。

这里使用 #{} 作为MyBatis的占位符,类似于之前的删除操作。它会在执行时将参数替换为实际值。具体字段说明:

  • #{carNum}:汽车编号
  • #{brand}:品牌
  • #{guidePrice}:指导价格
  • #{produceTime}:生产日期
  • #{carType}:汽车类型
  • #{id}:记录的唯一标识符,即要更新的汽车记录的 id

2. Java代码中的更新操作

Car car = new Car(4L, "9999", "凯美瑞", 30.3, "1999-11-10", "燃油车");
int count = sqlSession.update("updateById", car);
  • 这里通过 new Car() 创建了一辆 Car 对象,包含了所有要更新的属性。各个属性与Mapper XML中的占位符对应。

    • 4L:表示汽车的 id,也就是SQL语句中 id 字段要更新的记录。(null 不能赋值给基本类型 long,但可以赋值给 Long 这样的包装类,所以使用)
    • "9999":对应 carNum,汽车编号。
    • "凯美瑞":对应 brand,汽车品牌。
    • 30.3:对应 guidePrice,指导价格。
    • "1999-11-10":对应 produceTime,生产日期。
    • "燃油车":对应 carType,汽车类型。
  • 然后调用 sqlSession.update("updateById", car); 进行更新操作。MyBatis会将 car 对象的属性传递给对应的SQL语句中的占位符,生成最终的SQL语句并执行更新。

3. 详细流程说明

  • MyBatis会根据Java代码中的 car 对象的属性,替换Mapper XML中的占位符,生成最终的SQL语句。
  • 假设 car 对象的 id 是4,那么生成的SQL语句大致如下
    update t_car set
        car_num='9999',
        brand='凯美瑞',
        guide_price=30.3,
        produce_time='1999-11-10',
        car_type='燃油车'
    where
        id = 4;
    
  • 执行这条SQL语句后,t_car 表中 id 为4的记录就会被更新成新值。
  • 更新后,sqlSession.update() 方法会返回受影响的行数,即 count。

select查找操作 

1. MyBatis 中的 select 标签

  • 作用:MyBatis 使用 select 标签来编写 SQL 查询语句,定义如何从数据库中获取数据。在这个例子中,它用于根据 id 字段查询 t_car 表中的记录。

  • SQL 语句解释:

    <select id="selectById" resultType="com.powernode.mybatis.pojo.Car">
        select * from t_car where id = #{id}
    </select>
    
  • id 属性:id="selectById" 是一个唯一标识符,表示这条 SQL 语句在 MyBatis 配置中的 ID。当你在代码中调用 sqlSession.selectOne("selectById", 参数) 时,MyBatis 会根据这个 ID 找到对应的 SQL 语句。
  • SQL 查询:select * from t_car where id = #{id} 是实际的 SQL 语句。#{id} 是 MyBatis 的动态 SQL 占位符,它会在运行时将你传入的参数(比如 1)替换为实际的查询条件。
    • #{} 是 MyBatis 语法,用于从传递的参数中获取值,防止 SQL 注入。
    • select * 是查询所有列,你可以根据需要替换为具体的列名(例如:select id, name, brand)。

2. resultType 属性

  • 作用:resultType 属性告诉 MyBatis 查询结果应该映射成什么样的 Java 对象。也就是说,当从数据库中获取到数据后,MyBatis 会根据 resultType 中指定的类型来创建该类型的对象,并将查询结果封装到这个对象中。

  • 在此例子中的使用:

    resultType="com.powernode.mybatis.pojo.Car"
    
  • 这里 resultType="com.powernode.mybatis.pojo.Car" 指定了 Car 类为查询结果的映射对象类型。即查询到的 t_car 表中的记录会被封装成 Car 类的实例。
  • 全限定类名:com.powernode.mybatis.pojo.Car 是 Car 类的全限定类名,即包括了包名和类名。这是 MyBatis 推荐的写法,因为这样可以确保避免类名冲突,并确保 MyBatis 能找到正确的类。
  • resultType 的作用机制:

    • 查询结果(即数据库中的一条记录)会被转换为 Java 对象。MyBatis 会根据数据库表中的列名与 Car 类的属性名进行匹配,如果列名与属性名相同,MyBatis 就会自动将查询结果中的列值赋给对应的属性。
    • 例如:假设 t_car 表有以下列:
      • id,name,brand
      • 对应 Car 类的属性为:id,name,brand,MyBatis 会自动将数据库中的 id 列的值赋给 Car 类中的 id 属性,依此类推。
  • 注意事项:

    • 如果数据库中的列名和 Java 类中的属性名不一致,你需要使用别名(AS)或者 resultMap 进行手动映射。
      • 1. 假设的数据库表 t_car
        CREATE TABLE t_car (
            car_id INT PRIMARY KEY,
            car_name VARCHAR(50),
            car_brand VARCHAR(50)
        );
        
      • 在这个表中,列名是 car_id, car_name, 和 car_brand。
      • Java 类 Car

        public class Car {
            private int id;
            private String name;
            private String brand;
        
            // Getters and setters
            public int getId() {
                return id;
            }
        
            public void setId(int id) {
                this.id = id;
            }
        
            public String getName() {
                return name;
            }
        
            public void setName(String name) {
                this.name = name;
            }
        
            public String getBrand() {
                return brand;
            }
        
            public void setBrand(String brand) {
                this.brand = brand;
            }
        }
        

        在 Java 类 Car 中,属性名分别是 id, name, 和 brand,显然与数据库表 t_car 的列名不一致。

        解决办法:使用 resultMap 进行手动映射

        由于列名和属性名不一致,直接使用 resultType 是无法自动映射的,这时我们可以使用 resultMap 进行手动映射。

    • 3. MyBatis 映射配置:起别名

      <select id="selectById" resultType="com.powernode.mybatis.pojo.Car">
          select car_id as id, car_name as name, car_brand as brand
          from t_car
          where car_id = #{id}
      </select>
      

      在这个配置中:

      • car_id as id:把 car_id 列的查询结果映射为 id,从而与 Java 类 Car 的 id 属性对应。
      • car_name as name:把 car_name 列的查询结果映射为 name,与 Car 类中的 name 属性对应。
      • car_brand as brand:把 car_brand 列的查询结果映射为 brand,与 Car 类中的 brand 属性对应。
      • SQL 查询:select * from t_car where car_id = #{id},通过 #{id} 来传递 Java 方法中的参数。

    • 这样,通过使用 AS 设置别名,可以直接在 SQL 语句中将数据库的列名与 Java 类的属性名进行对应,而无需修改数据库或 Java 类。

3. selectOne 方法

  • 作用:selectOne 是 MyBatis 提供的一个方法,用于执行返回单条记录的查询。当我们确定查询的结果是唯一的(例如,查询主键),可以使用这个方法。如果查询结果返回多条记录,MyBatis 会抛出异常(TooManyResultsException)。

  • 用法解释:

Car car = sqlSession.selectOne("selectById", 1);
  • sqlSession 是 MyBatis 中操作数据库的会话对象,类似于 JDBC 中的 Connection 对象。
  • selectOne("selectById", 1):调用了 selectById 这个 SQL 查询,并传递了参数 1。这个 1 会替换 SQL 中的 #{id} 占位符,最终执行的 SQL 会是:
    select * from t_car where id = 1;
    

    返回的结果会是一个 Car 对象,存储在变量 car 中。

 select查所有的数据

sql语句

<select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
    select
        id,
        car_num as carNum,
        brand,
        guide_price as guidePrice,
        produce_time as produceTime,
        car_type as carType
    from t_car
</select>

在这个查询中,使用了别名将数据库表中的列名映射到 Java 类 Car 中的属性名:

  • car_num as carNum:将数据库中的 car_num 列映射到 Car 类中的 carNum 属性。
  • guide_price as guidePrice:将数据库中的 guide_price 列映射到 Car 类中的 guidePrice 属性。
  • produce_time as produceTime:将数据库中的 produce_time 列映射到 Car 类中的 produceTime 属性。
  • car_type as carType:将数据库中的 car_type 列映射到 Car 类中的 carType 属性。

Java 代码中的 selectList 调用 

List<Car> cars = sqlSession.selectList("selectAll");
  • selectList 方法:MyBatis 中的 selectList 方法用于返回一个 List 集合,集合中的每个元素是从数据库查询到的每一条记录的 Java 对象。在这个例子中,查询结果将会封装为一个 Car 对象的列表。

  • resultType 属性:在 SQL 配置中,resultType 依然用于指定封装查询结果的 Java 类类型,这里是 com.powernode.mybatis.pojo.Car,表示查询结果会封装成 Car 类的对象。

注意事项

  • resultType 指定的类型:需要注意的是,resultType 并不是指定集合的类型,而是指定集合中的每个元素类型。在这个例子resultType="com.powernode.mybatis.pojo.Car",意味着 MyBatis 会将查询结果的每一条记录封装为 Car 对象。

  • selectList 返回的结果:调用 selectList("selectAll") 会返回一个 List 集合,集合中的每个元素对应于查询结果中的一条记录,并会封装为指定的 Car 对象。

 

 

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐