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 ]}
【注意】无论是一对多,还是多对一,都可以采用分步加载,只要使用分步加载,就能实现懒加载

浙公网安备 33010602011771号