目录
一. 参数传递
1. 单个参数
单个参数可以直接传递,在映射文件中指定形参类型即可
基本类型:在配置文件指定基本类型即可
引用类型:mybatis将java中常用的引用类型定义了别名,我们在传引用类型时,形参的类型可以不用写全类名,直接写mybatis中定义的别名即可(一般是将首字母小写了),定义的别名可以在源码中查看
举例单个参数传递
1. 单参数基本类型
package com.ffyc.mybatis.dao;
import com.ffyc.mybatis.model.Admin;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface AdminDao {
Admin findAdminById(int id); //单参数基本类型
}
<!--在parameterType中指明基本类型即可-->
<select id="findAdminById" parameterType="int" resultType="Admin">
select account,password,gender,admin_age from admin where id = #{id}
</select>
2. 单参数引用类型
package com.ffyc.mybatis.dao;
import com.ffyc.mybatis.model.Admin;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface AdminDao {
Admin findAdminByAccount(String account); //单参数引用类型
}
<!--这里可以看到只用在parameterType中指明形参的别名即可-->
<select id="findAdminByAccount" parameterType="string" resultMap="adminmap">
select * from admin where account = #{account}
</select>
2. 多个参数
要传递多个参数时有两种方法
1. 利用@Param("与sql中的筛选条件名相同")注解标签来传递
public interface AdminDao {
Admin login(@Param("acc") String account, @Param("psd") String password);
}
标签中的acc和psd要和配置文件中的sql条件名相同
<select id="login" resultType="Admin">
select * from admin where account = #{acc} and password = #{psd}
</select>
2. 将多个参数封装到一个对象中传递
public interface AdminDao {
void savedAdmins(Admin admin);
}
<insert id="savedAdmins" parameterType="Admin">
insert into admin(account,password,gender,admin_age)
values ('${account}','${password}','${gender}','${adminAge}')
</insert>
public class Test2 {
public static void main(String[] args) throws IOException {
/*
SqlSession对象是与数据库交互的,每次与数据库连接,都需要创建一个新的连接对象,用完关闭即可
*/
SqlSession sqlSession = MybatisUtil.getSqlSession();
//获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
//封装对象
Admin admin = new Admin();
admin.setAccount("admin2");
admin.setPassword("222");
admin.setGender("男");
admin.setAdminAge(23);
//调用方法
adminDao.savedAdmins(admin);
//切记增删改一定要自己提交事务
sqlSession.commit();
//关闭sqlSession对象
sqlSession.close();
}
}
注意:
传多个参数时,如果是利用封装对象的方式,那么在sql中#{}或${}里面的值和对象中的属性名相同
如果是利用注解方式,那么sql中#{}或${}里面的值和注解中的名字相同
总结:
如果只是传递单个参数,那么直接传递,在标签中利用parameterType属性指定参数类型即可
如果是传递多个参数,可根据情况自己选择一种方式,一般建议传2-3个参数并且不方便在创建一个类进行封装时可以直接用注解标签,但如果传递的参数比较多,并且他们之间有关系,封装成一个对象方便时,那么可以在idea中在创建一个类,来单独封装该对象
二. mybatis增删改
1. 新增
在mybatis中给数据库中增加一条数据,可以用<insert></insert>标签
<insert id="savedAdmins" parameterType="Admin">
insert into admin(account,password,gender,admin_age)values ('${account}','${password}','${gender}','${adminAge}')
</insert>
public interface AdminDao {
void savedAdmins(Admin admin);
}
id和接口中的方法名相同,但此时还有一个要求,就是如果我们给数据库新增一条数据的同时,还想获得数据库给该条数据自动生成的主键id,以便我们可以在后续更好的找到该条数据的其他信息
要解决这个问题只需要在<insert></insert>标签中加入三个属性值即可
<insert id="savedAdmins" parameterType="Admin" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into admin(account,password,gender,admin_age)values ('${account}','${password}','${gender}','${adminAge}')
</insert>
这三个属性值分别是
useGeneratedKeys="true",keyProperty="id", keyColumn="id"
useGenerateKeys:(仅适用于 insert 和 update),这会令 MyBatis使用JDBC的getGeneratedKeys方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false
keyProerty:java类中定义的key属性,比如id属性
keyColumn:数据库中自增的那一列,比如id列
keyProerty和keyColumn表示映射关系
设置这三个属性mybatis就会帮我们把新增加的数据的id自动封装到java对象中,以便我们后续利用id进行增删改查操作
#{}和${}的区别:
我们在用mybatis传值时,经常会用到#{}和${}两种传值方式,那么这两种传值方式有什么区别呢?
#{}:主要用于在mybatis中传具体的值时,底层是JDBC的prepareStatement,是一种预编译的方式,更加安全,可以防止sql注入
${}:主要用于传数据库中的某一个具体的列,底层是JDBC的Statement方式,是一种拼接字符串的形式,虽然也可以传具体的值,但是需要我们自己去拼接,既麻烦又不安全,所以一般只建议传数据库中具体的某一列
2. 删除
在mybatis中删除一条数据是用<delete></delete>标签
public interface AdminDao {
void deleteById(int id);
}
<delete id="deleteById" parameterType="int">
delete from admin where id = #{id}
</delete>
3. 修改
在mybatis中修改一条数据是用<update></update>标签
public interface AdminDao {
void updateAdmin(Admin admin);
}
<update id="updateAdmin" parameterType="Admin">
update admin set account = #{account},password = #{password}, gender = #{gender} where id=#{id}
</update>
三. mybatis查询
1. 单张表查询
单表查询分为返回结果为一行一列的一个值,和返回结果为一个对象,即至少是一行多列
1. 返回结果为一个值
public interface AdminDao {
int findAdminCount();
}
<select id="findAdminCount" resultType="int">
select count(*) from admin
</select>
2. 返回结果至少是一个对象
public interface AdminDao {
Admin findAdminById(int id);
}
<select id="findAdminById" parameterType="int" resultType="Admin">
select account,password,gender,admin_age from admin where id = #{id}
</select>
注意:当返回结果是一个对象时,必须保证resultType返回值的类型与数据库中表的类型对应,并且该对象中的每个属性要与数据库表中的每一列名字相同,这样mybatis才能帮我们自动将查询到的结果封装到一个对象中
但是要想使对象中的属性名和数据库中的列名完全相同显然是不可能的,因为不同的语言命名的规范不同,java中都是驼峰式,而数据库中是用下划线_分隔开,那么我们如何解决命名不同导致无法自动封装的问题,其实有两种解决方法
1.在mybatis全局配置文件中用<setting>标签开启数据库列名与java属性名之间的转换
<!--开启数据库列名 与 java属性名之间的转换 user_name userName-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
只需要在<setting>标签中将mapUnderscoreToCamelCase属性设置为true,就可以让mybatis帮我们自动将查询结果封装到一个对象中
注意: 用上述方法的前提是数据库和java都是采用标准命名方式,即数据库是用下划线_区分,而java是驼峰式 eg:user_name userName
2. 在查询语句中给列名和属性名不同的起别名,将列名和属性名统一即可
<select id="findAdminById" parameterType="int" resultType="Admin">
select account,password,gender,admin_age as adminAge from admin where id = #{id}
</select>
如上述代码将admin_age这一列重新命名为adminAge这样就和属性中的值相同,mybatis就可以自动帮助我们封装到一个对象中
2. 多表查询
1. resultMap的用法
resultMap是结果映射,也可以帮我们把对象中的属性和数据库中的列一一对应起来进行封装
比如:上述的单表查询案例中,如果我既没有定义别名,也没有开启<setting>标签,那么其实还可以用resultMap将查询到的结果自己手动的进行封装,注意这只是举例说明resultMap是如何使用的,实际上resultMap并不用于单表映射结果,而是用于多表关联查询时映射结果
package com.ffyc.mybatis.dao;
import com.ffyc.mybatis.model.Admin;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface AdminDao {
Admin findAdminByAccount(String account);
}
<!--
结果比较特殊时,可以在resultMap标签中进行映射
-->
<resultMap id="adminmap" type="Admin">
<id property="id" column="id"></id><!--映射主键-->
<result property="account" column="account"></result>
<result property="password" column="password"></result>
<result property="gender" column="gender"></result>
<result property="adminAge" column="admin_age"></result>
</resultMap>
<select id="findAdminByAccount" parameterType="string" resultMap="adminmap">
select * from admin where account = #{account}
</select>
2.1 查询单个学生信息
每个学生有一个专业,一个专业可以对应多个学生,我们定义学生类和专业类,还有数据库中的学生表和专业表
package com.ffyc.mybatis.model;
public class Student {
private int id;
private String name;
private String gender;
private int num;
//private int mid;
//private String mname;//封装查询到的专业名字
private Major major; //类与类之间的关联关系 has-a关系 什么有什么 将专业信息封装到专业对象中,减少属性冗余
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 getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public Major getMajor() {
return major;
}
public void setMajor(Major major) {
this.major = major;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
", num=" + num +
", major=" + major +
'}';
}
}
以上是学生类的定义,在一个学生类中定义了一个Major类型的major属性表示专业,属于多对一的关系,专业属性定义如下
package com.ffyc.mybatis.model;
import java.util.List;
public class Major {
private int id;
private String name;
private List<Student> students;
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
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;
}
@Override
public String toString() {
return "Major{" +
"id=" + id +
", name='" + name + '\'' +
", students=" + students +
'}';
}
}
专业类中有专业id,专业名字,和这个专业有哪些学生,属于一对多的关系
现在通过id查询学生的信息,并且查询学生所在专业的名字
import com.ffyc.mybatis.model.Student;
import java.util.List;
public interface StudentDao {
//通过id查找单个学生
Student findStudentById(int id);
}
<!--自定义结果映射关系-->
<resultMap id="studentMap" type="Student"> <!--最终返回一个学生对象,将学生信息封装到学生对象中-->
<id property="id" column="id"></id>
<result property="num" column="num"></result>
<result property="name" column="name"></result>
<result property="gender" column="gender"></result>
<!--自动创建一个major对象,将major信息封装到major对象中,并把major对象赋给学生对象中的major属性-->
<association property="major" javaType="Major">
<result property="name" column="mname"></result>
</association>
</resultMap>
<select id="findStudentById" resultMap="studentMap">
SELECT s.id,s.num,s.name,s.gender,m.name mname FROM student s LEFT JOIN major m ON s.majorid=m.id where s.id=#{id}
</select>
由于此sql属于关联查询,所以mybatis不会帮我们封装专业信息,所以我们应该用resultMap自己手动封装学生里面的专业信息如上述代码
注意:如果用到resultMap,那么在select标签中就要将原来的返回值resltType属性改为resultMap,并于相关的resultMap标签绑定即可
通过上述代码有人就会想到,学生信息中定义的属性和数据库中的列是相同的,那么我们能不能不写学生信息的映射关系,只写学生中专业的映射关系呢?要想知道这个答案我们来看一下mybatis自动映射级别
2.2 mybatis自动映射级别
在mybatis中关于自动映射是可以设置级别的
- NONE:完全关闭自动映射,即使是单张表的查询也需要手动自己映射
- PARTIAL:部分映射,当查询不包含关联(嵌套)查询时,会自动映射,如果包含关联(嵌套)查询,就不会自动映射,需要我们用resultMap映射
- FULL:无论是否有关联(嵌套)查询都会自动映射
在mybatis的全局配置文件中利用<setting>标签可以设置这三种映射级别
<settings>
<!--
设置自动映射级别:
NONE: 完全关闭自动映射
PARTIAL: 当查询没有嵌套查询时会自动映射,一旦有嵌套查询,就不会自动映射
FULL: 无论是否有嵌套查询都会自动映射
mybatis官方默认的是部分映射 PARTIAL,所以该配置一般不用写
<setting name="autoMappingBehavior" value="PARTIAL"/>
-->
<setting name="autoMappingBehavior" value="PARTIAL"/>
</settings>
如上述代码,通过设置<setting>标签中的name属性autoMappingBehavior的value就可以设置三种级别的映射
在mybatis中默认的是PARTIAL部分映射所以上述的代码中关于学生部分的映射也需要我们自己去写,因为部分映射时有关联查询就不会帮我们自动映射了,有人可能会说那为什么不用FULL,他不是可以帮我们完全映射吗?
<settings>
<!--
设置自动映射级别:
NONE: 完全关闭自动映射
PARTIAL: 当查询没有嵌套查询时会自动映射,一旦有嵌套查询,就不会自动映射
FULL: 无论是否有嵌套查询都会自动映射
mybatis官方默认的是部分映射 PARTIAL,所以该配置一般不用写
<setting name="autoMappingBehavior" value="PARTIAL"/>
-->
<setting name="autoMappingBehavior" value="FULL"/>
</settings>
先将自动映射级别设置为FULL,然后调用刚才的findStudentById方法,我们来看一下结果
这个是结果,可以看出我们没查询Major的id,但是由于是FULL映射级别,并且我们专业和学生的id在数据库中都定义为id列,所以在映射时就将学生的id也一并映射给了专业,这显然是不正确的,因为在数据库中计算机专业的id是1,而我们要查询的学生的id是2
所以我们平时要是用到FULL级别的映射关系时,一定要注意定义列名时一定不要重复,否则可能导致错误,其实一般用默认的PARTIAL就够了,我们也不用去设置这个映射级别
2.3 查询所有学生信息
<!--自定义结果映射关系-->
<resultMap id="studentMap" type="Student"> <!--最终返回一个学生对象,将学生信息封装到学生对象中-->
<id property="id" column="id"></id>
<result property="num" column="num"></result>
<result property="name" column="name"></result>
<result property="gender" column="gender"></result>
<!--自动创建一个major对象,将major信息封装到major对象中,并把major对象赋给学生对象中的major属性-->
<association property="major" javaType="Major">
<result property="name" column="mname"></result>
</association>
</resultMap>
<select id="findStudents" resultMap="studentMap">
SELECT s.id,s.num,s.name,s.gender,m.name mname FROM student s LEFT JOIN major m ON
s.majorid=m.id
</select>
public interface StudentDao {
//查找所有学生
List<Student> findStudents();
}
只需要将查询单个学生的条件去掉,并且返回值为学生集合即可,映射resultMap可以共用一个,这是因为查询单个和查询多个封装的学生信息相同,mybatis会将查出的所有学生一个一个封装到学生对象中,并存到一个集合给我们返回
3. 嵌套查询
嵌套查询: 将一条关联查询分成多个单表查询,这样我们就可以不用写映射关系
3.1 查询单个学生信息
<resultMap id="studentMap1" type="Student">
<association property="major" javaType="Major" select="findMajorById"
column="majorid"></association>
</resultMap>
<select id="findStudentById1" resultMap="studentMap1">
SELECT * FROM student WHERE id=#{id}
</select>
<select id="findMajorById" resultType="Major" parameterType="int">
select name from major where id=#{id}
</select>
3.2 查询多个学生信息
<resultMap id="studentMap1" type="Student">
<association property="major" javaType="Major" select="findMajorById"
column="majorid"></association>
</resultMap>
<select id="findStudentById1" resultMap="studentMap1">
SELECT * FROM student WHERE id=#{id}
</select>
<select id="findStudents1" resultMap="studentMap1">
select * from student
</select>
同理,查询多条学生信息时,只需要将查询学生信息的sql中where后的条件去掉,然后将接口的返回值改为List<Student>即可
4. 嵌套查询和一条sql查询的区别
我们通过一个查询案列来看两者的区别,查询专业和该专业下的所有学生
4.1 一条sql查询
1. 通过Id查询专业下的所有学生
<resultMap id="majorMap" type="Major">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<collection property="students" javaType="list" ofType="Student">
<result property="num" column="num"></result>
<result property="name" column="sname"></result>
</collection>
</resultMap>
<select id="findMajorById" resultMap="majorMap" parameterType="int">
SELECT m.id,m.name,s.num,s.name sname FROM major m LEFT JOIN student s ON m.id=s.majorid WHERE m.id=#{id}
</select>
public interface MajorDao {
Major findMajorById1(int id);
}
由于一个专业可以有很多学生,所以我们在专业中定义的学生属性是有一个集合,resultMap中就不能用<association>标签,这个标签用于一对一的关系时的映射,而一对多的关系要用<collection>标签
其中property属性是专业中定义的students属性,javaType是list因为是集合,存储多个学生,ofType是集合中存储的类型
2. 查询所有专业对应的学生
<resultMap id="majorMap" type="Major">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<collection property="students" javaType="list" ofType="Student">
<result property="num" column="num"></result>
<result property="name" column="sname"></result>
</collection>
</resultMap>
<select id="findMajors" resultMap="majorMap">
SELECT m.id,m.name,s.num,s.name sname FROM major m LEFT JOIN student s ON m.id=s.majorid
</select>
只需要将查询单个学生信息中的where条件去掉即可
4.2 嵌套查询
1. 查询单个学生信息
<!--嵌套查询-->
<resultMap id="findMajorById1" type="Major">
<collection property="students" javaType="list" ofType="Student" select="findMajors1" column="id"></collection>
</resultMap>
<select id="findMajorById1" resultMap="findMajorById1">
select id,name from major where id=#{id}
</select>
<select id="findMajors1" resultType="Student">
select * from student where majorid = #{mid}
</select>
public interface MajorDao {
Major findMajorById1(int id);
}
如果按上述这样查询id会被当做条件,去另一条sql中查询,这样mybatis在封装时就不会对id列进行封装了
可以看到查询结果中并没有将专业id封装到major对象中,解决方法有两种
1. 自己手动将id映射
<resultMap id="findMajorById1" type="Major">
<id property="id" column="id"></id> <!--自己手动封装id-->
<collection property="students" javaType="list" ofType="Student" select="findMajors1" column="id"></collection>
</resultMap>
在resultMap中自己手动封装即可
可见id被成功封装
2.查询两次id,给其中一个起别名作为另一个sql的查询条件,另一个用于封装
<!--嵌套查询-->
<resultMap id="findMajorById1" type="Major">
<collection property="students" javaType="list" ofType="Student" select="findMajors1" column="mid"></collection>
</resultMap>
<select id="findMajorById1" resultMap="findMajorById1">
select id, id mid,name from major where id=#{id}
</select>
<select id="findMajors1" resultType="Student">
select * from student where majorid = #{mid}
</select>
同样可以成功映射
2. 查询多条学生信息
public interface MajorDao {
List<Major> findMajorsList();
}
<!--嵌套查询返回集合-->
<resultMap id="findMajorsList" type="Major">
<collection property="students" javaType="list" ofType="Student" column="id" select="findMajorList1">
</collection>
</resultMap>
<select id="findMajorsList" resultMap="findMajorsList">
select * from major
</select>
<select id="findMajorList1" resultType="Student">
select * from student where majorid = #{id}
</select>
四. mybatis注解方式写sql
mybatis提供了在接口上利用注解的方式写sql,这样就可以不在映射文件中写sql,但是建议只用注解的方式写简单的sql,比如单张表的查询,因为多张表的查询要用到resultMap,而在注解中就需要拼接字符串,并且sql很长会不好方便维护,所以复杂的sql还是用映射文件来写
4.1 注解增删改查
public interface MajorDao {
//通过id查询专业
@Select("select * from major where id=#{id}")
Major findMajorById2(int id);
//查询所有专业
@Select("select * from major")
List<Major> findMajors11();
//通过id删除专业
@Delete("delete from major where id=#{id}")
void deleteMajor(int id);
//新增专业
@Insert("insert into major(name)values('数学')")
void insertMajor();
//根据id修改专业名称
@Update("update major set name='土木' where id=#{id}")
void updateMajor(int id);
}