Mybatis 代码编写

一、#{ } 与${ }的区别


  #表示占位符 '?'这个字符,$表示简单的字符串拼接,如果是字符串需要自己在${}外侧添加单引号,存在sql注入的风险。


【注意】虽然大部分情况下使用#{ }是推荐的选择,但对于模糊查询 like '%#{keyword}%'会被解析为like '%?%'
如果你要使用模糊查询,请使用like '%${keyword}%' 或者MySQL的函数concat 即 like concat('%',#{keyword},'%')
或者"%"#{keyword}"%"

 

【注意】执行批量删除操作,要使用这种sql语句。delete from table where id in (${ids})
原因:如果使用#{}表示ids则会变成字符串

 

【注意】查询指定表的数据,也需要使用${}
原因:#{}表示的数据都会有两个单引号,mysql不允许表是字符串

 

二、@Param注解
  当你的sql语句只有一个参数,不需要使用该注解;但有多个参数时,mybatis并不知道这些参数应该放在sql里的哪些实际位置
所以需要用这个注解标注,在mapper映射文件中,就可以使用这两个注解里的值

1 public interface UserMapper(){
2   public User getUserByAddressandScore(@Param("address")String address, @Param("score")Double score);
3 }

扩展:如果你实在不想用@param这个注解,可以使用arg0,arg1...或者param1,param2...,这是因为mybatis把你的参数放在了Map里
它们会顺序代替你给的参数值,在这里arg0代表address,arg1代表score,param同理。


三、使用Map集合传入参数
  Map是天然的key-value,可以保证参数位置正确

 

四、使用实体类传入参数

  实体类对象.字段,可以保证参数位置正确【但要保证get,set方法已经设置且方法名符合规范】


                                        Mybatis类型别名

别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer

int

_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator


五、如果我们查询出来的结果没有对应的实体类作为映射,我们可以设置返回类型为map【限一条数据,@MapKey除外】

1 <select id="getUserByIdToMap" resultType="map">
2     select * from t_user where id = #{id}
3 </select>
1 public static void main(String[] args) throws IOException {
2     InputStream is = Resources.getResourceAsStream("mybatis-core.xml");
3     SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
4     UserMapper mapper = builder.build(is).openSession(true).getMapper(UserMapper.class);
5     System.out.println(mapper.getUserByIdToMap(1));
6 }
7 //output:{phone=111, sex=男, name=jack, id=1}

 

六、如果查询多条数据如果也想使用map,可以使用List<Map<String,Object>>

 

七、@MapKey注解的使用
  如果我们查询多条数据,仍然希望使用Map,可以从查询到的数据中选一个字段作为map的key【这个字段是唯一的】

1 <select id="getAllUserToMap" resultType="map">
2     select * from t_user;
3 </select>
1 @MapKey(value = "id")
2 Map getAllUserToMap();
 1 public static void main(String[] args) throws IOException {
 2   InputStream is = Resources.getResourceAsStream("mybatis-core.xml");
 3   SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
 4   UserMapper mapper = builder.build(is).openSession(true).getMapper(UserMapper.class);
 5   System.out.println(mapper.getAllUserToMap());
 6 }
 7 //output:
 8 {
 9   1={phone=111, sex=男, name=jack, id=1},
10   2={phone=222, sex=男, name=jacks, id=2},
11   3={phone=065, sex=女, name=zha, id=3},
12   4={phone=434, sex=女, name=sss, id=4},
13   5={phone=3434, sex=女, name=sad, id=5},
14   6={phone=661, sex=男, name=vv2, id=6},
15   7={phone=1020, sex=男, name=vv3, id=7},
16   8={phone=2131, sex=男, name=vv4, id=8},
17   9={phone=313135, sex=男, name=vv10, id=9}
18 }

 

八、获取自动递增的主键
【情景导入】当我们要开发一个页面,输入班级号后然后在班级添加学生,那么我们需要两个表
Table1:Class{classID,capacity,number,grade}
Table2:Student{studentID,name,gender,classID}
如果我们创建的班级了班级,现在需要添加学生,那么我们需要获得班级的ID
那么我们怎么获取刚刚添加的班级的ID呢,显然我们需要其返回自增主键

首先,我们要添加班级

public int insertClass(MyClass myclass);
<!--keyProperty表示将classID返回到对象中,useGeneratedKeys表示sql将使用自增主键-->
<insert 
    id="insertClass" 
    useGeneratedKeys="true" 
    keyProperty="classID">
    insert into t_class (classID, capacity, students, grade) values(null,#{capacity},#{students},#{grade});
</insert>

 

进行测试

public static void main(String[] args) throws IOException {
    InputStream is = Resources.getResourceAsStream("mybatis-core.xml");
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    MyClass myClass = new MyClass(null,7,60,0);
    //注意:1是返回的受影响的行数,要返回主键,只能放在类的某个字段中,这里是放到了classID中
    builder.build(is).openSession(true).getMapper(ClassMapper.class)
        .insertClass(myClass);
    //可以看到本来myClass的classID没有值,插入到数据库再打印就有值了
    System.out.println(myClass.getClassID());
}
//output:1,4可以返回添加时的主键    

  


九、解决字段名与属性名不一致的问题
如果字段名与属性名不一致,则会查询不出结果,解决办法有三种

  • 在mapper.xml文件使用mysql别名as或空格关键字
  • 通过配置设置项实现自动转换驼峰命名法和下划线命名法
1 <settings>
2     <!-- emp_name -> empName -->
3     <setting name="mapUnderscoreToCamelCase" value="true"/>
4 </settings>
  • 在mapper.xml文件使用resultMap,resultMap一般用于处理一对多,多对一的关系

 

 1 <resultMap id="classResultMap" type="MyClass">
 2     <!--property代表JavaBean的属性,column代表数据库表中的字段-->
 3     <!--id专门设置主键的字段映射--->
 4     <id property="classID" column="classID"/>
 5     <result property="grade" column="grade"/>
 6     <result property="capacity" column="capacity"/>
 7     <result property="students" column="students"/>
 8 </resultMap>
 9 
10 <!--resultMap输入上面的id-->
11 <select id="queryAllClass" resultMap="classResultMap">
12         select * from t_class;
13 </select>    

【建议】所有映射都需要写,不能写一部分


十、多对一、一对多的关系
在开发中,类与类之间存在着某种关系,比如老师和学生
在处理多对一中,多的一方要拥有一的一方,就好比每一个学生类都要有一个老师作为其属性
在处理一对多中,一的一方要有多的集合,就好比老师拥有学生集合

【问题引入】如果同时联合查询多个表,就会返回很多信息,这时如果使用resultType属性解决不了问题,
因为Java Bean内的属性并不能对应查询出来的所有字段,这时应该使用resultMap。
下面的案例以老师和学生为例,介绍如何返回多表多个字段的方法解决多对一的问题,主要用到的还是association

 1 public class Student implements Serializable {
 2     private Integer sid;
 3     private String gender;
 4     private String name;
 5     private Integer classID;
 6     private MyClass myClass;
 7     //get set toString
 8 }
 9 
10 public class MyClass implements Serializable {
11     private Integer classID;
12     private Integer grade;
13     private Integer capacity;
14     private Integer students;
15     //get set toString
16 }

 

【方法一】使用级联属性

 1 <resultMap id="StudentAndClassMap" type="Student">
 2     <id property="sid" column="sid"/>
 3     <result property="gender" column="gender"/>
 4     <result property="name" column="name"/>
 5     <result property="classID" column="classID"/>
 6 <!--使用级联属性-->
 7     <result property="myClass.classID" column="classID"/>
 8     <result property="myClass.grade" column="grade"/>
 9     <result property="myClass.capacity" column="capacity"/>
10     <result property="myClass.students" column="students"/>
11 </resultMap>
12 <select 
13     id="getStudentAndClassInfo" 
14     resultMap="StudentAndClassMap">
15    select * from mbg.t_student join mbg.t_class tc on tc.classID = t_student.classID where sid = #{sid};
16 </select>

 

测试代码

public static void main(String[] args) throws IOException {
   InputStream is = Resources.getResourceAsStream("mybatis-core.xml");
  SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
  Student student = builder.build(is).openSession(true).getMapper(StudentMapper.class)
            .getStudentAndClassInfo(1);
  System.out.println(student);
}
//output:Student{sid=1, gender='男', name='小红', classID=1, myClass=MyClass{classID=1, grade=7, capacity=60, students=0}}

 

【方法二】嵌套resultMap+association

<resultMap id="StudentAndClassMap" type="Student">
  <id property="sid" column="sid"/>
  <result property="gender" column="gender"/>
  <result property="name" column="name"/>
  <result property="classID" column="classID"/>
  <association property="myClass" resultMap="classResultMap"/>
</resultMap>
<resultMap id="classResultMap" type="MyClass">
  <!--property代表JavaBean的属性,column代表数据库表中的字段-->
  <result property="classID" column="classID"/>
  <result property="grade" column="grade"/>
  <result property="capacity" column="capacity"/>
  <result property="students" column="students"/>
</resultMap>

 

【方法三】使用association标签+javaType属性

 1 <resultMap id="StudentAndClassMap" type="Student">
 2   <id property="sid" column="sid"/>
 3   <result property="gender" column="gender"/>
 4   <result property="name" column="name"/>
 5   <result property="classID" column="classID"/>
 6   <association property="myClass" javaType="MyClass">
 7     <id property="classID" column="classID"/>
 8     <result property="grade" column="grade"/>
 9     <result property="capacity" column="capacity"/>
10     <result property="students" column="students"/>
11   </association>
12 </resultMap>

 


【方法四】使用分步查询
查询学生信息

 1 <select id="getStudentByStep" resultMap="getStudentByStepMap">
 2   select * from mbg.t_student where sid=#{sid};
 3 </select>
 4 <resultMap id="getStudentByStepMap" type="Student">
 5   <id property="sid" column="sid"/>
 6   <result property="gender" column="gender"/>
 7   <result property="name" column="name"/>
 8   <result property="classID" column="classID"/>
 9 <!--select 表示分步查询第二步应该执行什么sql语句,column是多个类进行联系的关键字段-->
10 <!--注意:sql标识是全类名.sqlID-->
11   <association property="myClass" select="com.tust.mapper.ClassMapper.queryClassByID" column="classID"/>
12 </resultMap>

依据classID再次查询班级信息,完成二次查询,将查询出的结果注入到Student的myClass属性中

1 <select id="queryClassByID" resultType="MyClass">
2     select * from t_class where classID = #{classID}
3 </select>

进行测试

1 public static void main(String[] args) throws IOException {
2   InputStream is = Resources.getResourceAsStream("mybatis-core.xml");
3   SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
4   Student student = builder.build(is).openSession(true).getMapper(StudentMapper.class)
5               .getStudentByStep(1);
6   System.out.println(student);
7 }
8 //output:Student{sid=1, gender='男', name='小红', classID=1, myClass=MyClass{classID=1, grade=7, capacity=60, students=0}}

 

十一、延迟加载(也叫懒加载)


上面的分步查询其实是懒加载,就是说如果要查询的对象包含另一个对象(比如学生包含班级),
如果懒加载开启,如果只查询Student的独有字段则不会二次查询Class【只执行一次sql】,除非你要查询学生所在班级人数什么的才会再执行二次sql
如果懒加载关闭,无论查询Student的什么字段,都会【执行两次sql】
这种按需执行sql的机制可以减轻对数据库访问压力
关于懒加载更详细的教程:https://www.bilibili.com/video/BV1VP4y1c7j7?p=45&spm_id_from=pageDriver


关于懒加载相关的配置项

lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。 true | false false
aggressiveLazyLoading 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考lazyLoadTriggerMethods)。 true | false false (在 3.4.1 及之前的版本中默认为 true)



【注意】开启懒加载一定要把aggressiveLazyLoading关闭
【提示】开启懒加载全局都会懒加载,如果你有一些例外情况不需要执行懒加载,可以使用fetchType,具体所有方法如下:

1 <!--association的属性fetchType=eager表示此处不需要懒加载,fetchType=lazy表示此处是懒加载-->
2 <association
3   property="myClass"
4   select="com.tust.mapper.ClassMapper.queryClassByID"
5   column="classID"
6   fetchType="eager"
7 />

 

关于一对多关系的解决办法

 

 1 public class Student implements Serializable {
 2   private Integer sid;
 3   private String gender;
 4   private String name;
 5   private Integer classID;
 6   private MyClass myClass;
 7 //get set toString
 8 }
 9 
10 public class MyClass implements Serializable {
11   private Integer classID;
12   private Integer grade;
13   private Integer capacity;
14   private Integer students;
15   private List<Student> studentList;
16 //get set toString
17 }

 

 

 

mapper.xml编写

 1 <resultMap id="classResultMap" type="MyClass">
 2   <!--property代表JavaBean的属性,column代表数据库表中的字段-->
 3   <id property="classID" column="classID"/>
 4   <result property="grade" column="grade"/>
 5   <result property="capacity" column="capacity"/>
 6   <result property="students" column="students"/>
 7   <!--javaType代表返回的类型,这里已经知道是list,所以要使用ofType来指定集合里的泛型-->
 8   <!--使用collection处理一对多关系-->
 9   <collection property="studentList" ofType="Student">
10     <id property="sid" column="sid"/>
11     <result property="gender" column="gender"/>
12     <result property="name" column="name"/>
13     <result property="classID" column="classID"/>
14   </collection>
15 </resultMap>

 

进行测试

 1 public static void main(String[] args) throws IOException {
 2   InputStream is = Resources.getResourceAsStream("mybatis-core.xml");
 3   SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
 4   MyClass myClass = builder.build(is).openSession(true).getMapper(ClassMapper.class).
 5             queryClassByID(1);
 6   System.out.println(myClass);
 7 }
 8 //output:
 9 MyClass{classID=1, grade=7, capacity=60, students=0,
10 studentList=[
11   Student{sid=1, gender='男', name='小红', classID=1, myClass=null},
12   Student{sid=2, gender='女', name='小米', classID=1, myClass=null},
13   Student{sid=3, gender='女', name='小李', classID=1, myClass=null}
14 ]}

 

通过分步查询实现一对多关系
和分步查询多对一比较类似,也是使用select和column属性

 

 1 <resultMap id="classResultMap" type="MyClass">
 2   <!--property代表JavaBean的属性,column代表数据库表中的字段-->
 3   <id property="classID" column="classID"/>
 4   <result property="grade" column="grade"/>
 5   <result property="capacity" column="capacity"/>
 6   <result property="students" column="students"/>
 7   <!--select表示接下来调用的sql语句,column表示多表关联的id-->
 8   <collection property="studentList"
 9          ofType="Student"
10          select="com.tust.mapper.StudentMapper.getStudentByClass"
11          column="classID"
12   />
13 </resultMap>
14 
15 <select id="queryClassByStep" resultMap="classResultMap">
16   select * from t_class where classID = #{classID}
17 </select>

 

进行测试

 1 public static void main(String[] args) throws IOException {
 2   InputStream is = Resources.getResourceAsStream("mybatis-core.xml");
 3   SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
 4   MyClass myClass = builder.build(is).openSession(true).getMapper(ClassMapper.class).
 5               queryClassByStep(1);
 6   System.out.println(myClass);
 7 }
 8 output:
 9 MyClass{classID=1, grade=7, capacity=60, students=0,
10 studentList=[
11   Student{sid=1, gender='男', name='小红', classID=1, myClass=null},
12   Student{sid=2, gender='女', name='小米', classID=1, myClass=null},
13   Student{sid=3, gender='女', name='小李', classID=1, myClass=null}
14 ]}

【注意】无论是一对多,还是多对一,都可以采用分步加载,只要使用分步加载,就能实现懒加载 

posted @ 2022-03-18 21:46  清巡  阅读(215)  评论(0)    收藏  举报