一、增删改查
1、INSERT
<insert id="insert" parameterType="com.starfall.mybaits.entity.Employee">
insert into employee
(employee_id, first_name, last_name,
email, phone_number, hire_date,
job_id, salary, manager_id,
department_id)
values
(#{employeeId,jdbcType=INTEGER}, #{firstName,jdbcType=VARCHAR},
#{lastName,jdbcType=VARCHAR},
#{email,jdbcType=VARCHAR},
#{phoneNumber,jdbcType=VARCHAR}, #{hireDate,jdbcType=DATE},
#{jobId,jdbcType=VARCHAR}, #{salary,jdbcType=DECIMAL},
#{managerId,jdbcType=INTEGER},
#{departmentId,jdbcType=INTEGER})
</insert>
2、DELETE
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from
employee
where employee_id = #{employeeId,jdbcType=INTEGER}
</delete>
3、UPDATE
<update id="updateByPrimaryKey" parameterType="com.starfall.mybaits.entity.Employee">
update employee
set first_name = #{firstName,jdbcType=VARCHAR},
last_name =
#{lastName,jdbcType=VARCHAR},
email = #{email,jdbcType=VARCHAR},
phone_number = #{phoneNumber,jdbcType=VARCHAR},
hire_date =
#{hireDate,jdbcType=DATE},
job_id = #{jobId,jdbcType=VARCHAR},
salary =
#{salary,jdbcType=DECIMAL},
manager_id = #{managerId,jdbcType=INTEGER},
department_id = #{departmentId,jdbcType=INTEGER}
where employee_id =
#{employeeId,jdbcType=INTEGER}
</update>
4、SELECT
<select id="selectEmployee" resultType="com.starfall.mybaits.entity.Employee">
SELECT
employee_id AS employeeId,
first_name AS firstName,
last_name AS lastName,
email AS email,
phone_number AS phoneNumber,
hire_date AS hireDate,
job_id AS jobId,
salary AS salary,
manager_id AS managerId,
department_id AS departmentId
FROM
employee
WHERE
employee_id = #{employeeId}
</select>
5、测试
@Test
public void test03() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//1、获取到的SqlSession不会自动提交数据
SqlSession openSession = sqlSessionFactory.openSession();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//测试添加
Employee employee = new Employee(null, "jerry4",null, "1");
mapper.addEmp(employee);
System.out.println(employee.getId());
//2、手动提交数据
openSession.commit();
}finally{
openSession.close();
}
}
mybatis允许增删改直接定义以下类型返回值:Integer、Long、Boolean、void
需要手动提交数据:
- sqlSessionFactory.openSession();===》手动提交:openSession.commit();
- sqlSessionFactory.openSession(true);===》自动提交
二、自增主键
1、MySQL、SQL Server获取自增主键
mysql支持自增主键,自增主键值的获取,mybatis也是利用statement.getGenreatedKeys();
useGeneratedKeys="true";使用自增主键获取主键值策略
keyProperty;指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给javaBean的哪个属性。
<insert id="insertEmployee"
parameterType="com.starfall.mybaits.entity.Employee"
useGeneratedKeys="true" keyProperty="employeeId">
insert into employee(first_name,last_name,email)
values(#{firstName},#{lastName},#{email})
</insert>
2、Oracle获取非自增主键
(1)Oracle使用序列来模拟自增:
--下一个序列值
select employees_seq.nextval from dual
--当前序列值
select employees_seq.currval from dual
(2)mapper写法:
keyProperty: 查出的主键值封装给javaBean的哪个属性
order : "BEFORE":当前sql在插入sql之前运行
"AFTER":当前sql在插入sql之后运行
resultType: 查出的数据的返回值类型
AFTER示例:
<insert id="insertEmployee" parameterType="com.starfall.mybaits.entity.Employee"
databaseId="oracle">
<selectKey keyProperty="employeeId" order="BEFORE" resultType="Integer">
<!-- 编写查询主键的sql语句 -->
select employees_seq.nextval from dual
</selectKey>
insert into employees(employee_id,first_name,last_name,email)
values(#{employeeId},#{firstName},#{lastName},#{email})
</insert>
AFTER示例:
<insert id="insertEmployee" parameterType="com.starfall.mybaits.entity.Employee"
databaseId="oracle">
<selectKey keyProperty="employeeId" order="AFTER" resultType="Integer">
<!-- 编写查询主键的sql语句 -->
select employees_seq.currval from dual
</selectKey>
insert into employees(employee_id,first_name,last_name,email)
values(employees_seq.nextval,#{firstName},#{lastName},#{email})
</insert>
(3)BEFORE与AFTER区别:
BEFORE运行顺序:
先运行selectKey查询id的sql;查出id值封装给javaBean的id属性
在运行插入的sql;就可以取出id属性对应的值
BEFORE使用序列为:select employees_seq.nextval from dual
AFTER运行顺序:
先运行插入的sql(从序列中取出新值作为id);
再运行selectKey查询id的sql;
AFTER使用序列为:select employees_seq.currval from dual
建议使用BEFORE,因为在批量插入的时候,AFTER取出的序列可能是最后一个序列,而不是最近插入的一条记录的序列。
三、参数处理
1、单个参数
单个参数:mybatis不会做特殊处理。#{参数名/任意名}:取出参数值。
不需要使mapper接口中的参数名和mapper文件中的#{参数名}相同。
2、多个参数
(1)示例
接口:Employee selectEmployeeByIdAndLastName(Integer employeeId, String lastName);
SQL:
<select id="selectEmployeeByIdAndLastName" resultType="com.starfall.mybaits.entity.Employee">
SELECT
*
FROM
employee
WHERE
employee_id = #{employeeId}
AND last_name = #{lastName}
</select>
异常:
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'employeeId' not found. Available parameters are [arg1, arg0, param1, param2]
原因:多个参数会被封装成 一个map,
key:param1...paramN,或者参数的索引也可以
value:传入的参数值
#{}就是从map中获取指定的key的值;
(2)命名参数
明确指定封装参数时map的key;@Param("id")
public Employee getEmpByIdAndLastName(@Param("id")Integer id,@Param("lastName")String lastName);
3、参数类型
(1)、POJO
如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo;#{属性名}:取出传入的pojo的属性值
(2)、Map
如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map;#{key}:取出map中对应的值
(3)、TO
如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象
Page{
int index;
int size;
}
4、其他特殊场景
(1)、public Employee getEmp(@Param("id")Integer id,String lastName);
取值:id==>#{ id / param1 } lastName==>#{ param2 }
(2)、public Employee getEmp(Integer id,@Param("e")Employee emp);
取值:id==>#{ param1 } lastName===>#{ param2.lastName/e.lastName }
(3)、public Employee getEmpById(List<Integer> ids);
取值:取出第一个id的值: #{ list[0] }
##特别注意:如果是Collection(List、Set)类型或者是数组,也会特殊处理。也是把传入的list或者数组封装在map中。
key:Collection(collection),List(list), 数组(array)
5、源码分析参数处理
Debugger的源码:
Employee employee = mapper.selectEmployeeByIdAndLastName(100, "King");
Employee selectEmployeeByIdAndLastName(@Param("employeeId") Integer employeeId, @Param("lastName") String lastName);
5.1 在调用处打上debugger断点
5.2 step into 进入mapper.selectEmployeeByIdAndLastName方法:
调用MapperProxy<T>.invoke(Object, Method, Object[]) 方法:
其中,invoke方法的参数:
proxy:org.apache.ibatis.binding.MapperProxy@609bcfb6。mapper的代理对象。
method:mapper接口中的方法全类名。
public abstract com.starfall.mybaits.entity.Employee com.starfall.mybaits.dao.EmployeeMapper.selectEmployeeByIdAndLastName(java.lang.Integer,java.lang.String)
args:mapper接口中的方法的参数
5.2.1 进入第58行,获取mapperMethod:
第63行,首先从缓存中getmapperMethod,如果没有则进入第65行,根据mapper接口(反射对象)、mapper接口中的方法、sqlSession构造一个。
进入MapperMethod构造器:
其中有两个方法:获取SqlCommand对象和method对象。
5.2.1.1 第49行:new SqlCommand构造器,获取command对象:属性name为查询方法的全类名,type为SELECT。
5.2.1.2 第50行:new MethodSignature构造器,获取method对象:进入构造器查看:
整个MethodSignature构造器用了获取该查询方法的信息,例如返回类型(returnType)、是否返回void(returnsVoid)。
注意看第295行:ParamNameResolver参数解析器,进入其中看如何解析查询方法参数:
5.2.1.2.1 第54行,paramTypes:通过反射获取方法参数的类型数组。
5.2.1.2.2 第55行,paramAnnotations:通过反射获取方法参数的注解数组。
5.2.1.2.3 第59行开始,遍历参数数组
第65行开始,遍历该参数的注解数组,获取注解的value值,并且hasParamAnnotation属性设为true.
注意第75行,name = getActualParamName(method, paramIndex); 如果mybatis全局配置文件settings标签中配置了useActualParamName属性(允许使用方法签名中的名称作为语句参数名称)则name直接为参数的名称。
5.2.1.2.4 第83行,获取的name和参数下标索引put到map中。
5.2.1.2.5 第85行,最后返回names属性的map:private final SortedMap<Integer, String> names;
5.2.2 在5.2.1中获取完mapperMethod后,继续执行mapperMethod.execute(sqlSession, args);
5.2.2.1 在mapperMethod.execute()方法中,主要看下面代码:method.convertArgsToSqlCommandParam(args);
5.2.2.1.1 进入method.convertArgsToSqlCommandParam(args);
method对象是由 5.2.1.2 第50行:new MethodSignature构造器获取,进入MethodSignature类,发现是调用paramNameResolver.getNamedParams(args);方法。
5.2.2.1.1.1 进入paramNameResolver.getNamedParams(args);方法
注意两个属性:names和hasParamAnnotation,是在上方5.2.1中ParamNameResolver参数解析器中设置初始化的。names的值可以参考上方第5.2.1.2.5步。
a、注意第118行,如果参数没有注解,并且参数只有一个,则返回第一个key:args[0]:单个参数直接返回。
b、第123开始行遍历names集合:{0=employeeId, 1=lastName}
将names的value作为key(employeeId),args方法参数对应下标元素的值作为value(100)
c、第129行,同时添加mybatis默认的参数:param1=100
d、最后返回param的map集合:
至此参数解析完毕。
6、参数值的获取
(1)#{ }和${ }
1、都可以可以获取map中的值或者pojo对象属性的值
2、#{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入
${}:取出的值直接拼装在sql语句中;会有安全问题;
示例:
select * from tbl_employee where id=${id} and last_name=#{lastName}
Preparing: select * from tbl_employee where id=2 and last_name=?
(2)${ }使用场景
原生jdbc不支持占位符的地方我们就可以使用${}进行取值,比如分表、排序。。。;按照年份分表拆分。
select * from ${year}_salary where xxx;
select * from tbl_employee order by ${f_name} ${order}
(3)#{}:更丰富的用法
1、规定参数的一些规则
javaType、 jdbcType、 mode(存储过程)、 numericScale(数值类型,设置小数点后保留的位数)、resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能)
2、jdbcType
jdbcType通常需要在某种特定的条件下被设置。
在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);
JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理;
由于全局配置中:jdbcTypeForNull配置默认为 OTHER,而oracle不支持OTHER;
两种办法
1、在指定的列中指定jdbcType类型:#{email,jdbcType=NULL},或者为#{email,jdbcType=VARCHAR}。不为默认的OTHER。
2、在全局配置文件中设置空值指定 JDBC 类型:jdbcTypeForNull=NULL
<setting name="jdbcTypeForNull" value="NULL"/>