一、MyBatis的sql映射文件
sql映射文件的作用是指导mybatis如何进行数据库的增删改查。
二、sql映射文件中的增删改查案例(使用接口式编程)
项目目录结构:
数据库表设计:
JavaBean设计:
package com.csu.marden;
public class Employee {
private Integer id;
private String lastName;
private String email;
private String gender;
public Employee() {
}
public Employee(Integer id, String lastName, String email, String gender) {
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + "]";
}
}
properties文件编写:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
mybatis全局配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="dbconfig.properties"></properties>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<typeAlias type="com.csu.marden.Employee" />
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<!-- 将sql映射文件(EmployeeMapper.xml)注册到全局配置文件(mybatis-config.xml)中 -->
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration>
sql映射文件对应的接口:
package com.csu.marden;
public interface EmployeeMapper {
//根据id查询数据,并封装成Employee对象
public Employee getEmpById(Integer id);
//添加一个Employee对象
public void addEmp(Employee employee);
//修改一个Employee对象
public void updateEmp(Employee employee);
//根据id,删除一个Employee对象
public void deleteEmpById(Integer id);
}
sql映射文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.csu.marden.EmployeeMapper">
<!-- 查询方法 -->
<select id="getEmpById" resultType="employee" >
select * from tbl_employee where id = #{id}
</select>
<!-- 插入方法 -->
<insert id="addEmp" parameterType="employee">
insert into tbl_employee (last_name,email,gender) values (#{lastName},#{email},#{gender})
</insert>
<!-- 更新方法 -->
<update id="updateEmp" parameterType="employee">
update tbl_employee set last_name=#{lastName},email=#{email},gender=#{gender} where id=#{id}
</update>
<!-- 删除方法 -->
<delete id="deleteEmpById" parameterType="integer">
delete from tbl_employee where id=#{id}
</delete>
</mapper>
测试文件:
package com.csu.marden;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Test {
public static void main(String[] args) throws IOException {
//1.根据xml配置文件(全局配置文件,即mybatis-config.xml)创建一个SqlSessionFactory对象
String resources="mybatis-config.xml";
InputStream inputStream=Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//2.根据SqlSessionFactory创建SqlSession,每个SqlSession代表和数据库的一次会话
SqlSession openSession=sqlSessionFactory.openSession();
try{
//3.获取接口的实现类对象(mybatis会为接口自动创建一个代理对象,代理对象会执行增删改查)
EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class);
//4.通过接口的实现类对象调用接口的查询方法
// //查询操作
// Employee employee1=mapper.getEmpById(1);
// System.out.println(employee1);
// //添加操作
// Employee employee2=new Employee(null,"jerry","jerry@csu.edu.cn","1");
// mapper.addEmp(employee2);
// //更新操作
// Employee employee3=new Employee(1,"marden","marden@csu.edu.cn","0");
// mapper.updateEmp(employee3);
// //删除操作
// mapper.deleteEmpById(2);
//手动提交
openSession.commit();
}finally{
openSession.close();
}
}
}
三、insert标签获取主键的值
(1)支持自增主键的数据库,如MySQL,SQL Server。可以设置 useGeneratedKeys=“true”,然后再把 keyProperty 设置为目标属性就 OK 了
案例:MySQL支持自增主键,因此在数据库插值时,不需要指定主键值。但是在插入操作中,需要获取插入数据的主键值。
package com.csu.marden;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Test {
public static void main(String[] args) throws IOException {
//1.根据xml配置文件(全局配置文件,即mybatis-config.xml)创建一个SqlSessionFactory对象
String resources="mybatis-config.xml";
InputStream inputStream=Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//2.根据SqlSessionFactory创建SqlSession,每个SqlSession代表和数据库的一次会话
SqlSession openSession=sqlSessionFactory.openSession();
try{
//3.获取接口的实现类对象(mybatis会为接口自动创建一个代理对象,代理对象会执行增删改查)
EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class);
//4.通过接口的实现类对象调用接口的查询方法
//添加操作
Employee employee=new Employee(null,"jerry","jerry@csu.edu.cn","0");
mapper.addEmp(employee);
System.out.println(employee.getId());
openSession.commit();
}finally{
openSession.close();
}
}
}
结果:
分析:虽然添加数据记录成功,但想要获取插入记录的主键未成功,得到的是null
解决方案:在insert标签中添加useGeneratedKeys属性和keyProperty属性,即设置useGeneratedKeys=”true”,然后再把 keyProperty 设置为目标属性就 OK 了。
mysql支持自增主键的获取,mybatis同样利用statement.getGenreatedKeys()获取自增主键。
useGeneratedKeys=“true”:使用自增主键来获取主键值的策略。
keyProperty属性:对应获取到的主键值,mybatis将获取到的主键值,封装到JavaBean的指定属性中。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.csu.marden.EmployeeMapper">
<!-- 查询方法 -->
<select id="getEmpById" resultType="employee" >
select * from tbl_employee where id = #{id}
</select>
<!-- 插入方法 -->
<insert id="addEmp" parameterType="employee" useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee (last_name,email,gender) values (#{lastName},#{email},#{gender})
</insert>
<!-- 更新方法 -->
<update id="updateEmp" parameterType="employee" >
update tbl_employee set last_name=#{lastName},email=#{email},gender=#{gender} where id=#{id}
</update>
<!-- 删除方法 -->
<delete id="deleteEmpById" parameterType="integer">
delete from tbl_employee where id=#{id}
</delete>
</mapper>
结果:
(2)不支持自增主键的数据库,如Oracle。使用序列来模拟自增,每次插入的数据的主键是从序列中拿到的值。即使用selectKey子元素,selectKey子元素将会首先运行,id会被设置,然后插入语句会被调用。
案例:Oracle不支持自增主键,使用序列来模拟自增,每次插入的数据的主键都是从序列中拿到的值
<!-- MySQL插入方法 -->
<insert id="addEmp" parameterType="employee" useGeneratedKeys="true" keyProperty="id" databaseId="mysql">
insert into tbl_employee (last_name,email,gender) values (#{lastName},#{email},#{gender})
</insert>
<!-- Oracle插入方法 -->
<insert id="addEmp" parameterType="employee" databaseId="oracle">
<!-- keyProperty指查出的主键值封装给JavaBean的哪个属性;order指当前sql在插入sql之前运行;resultType指查处的数据的返回类型 -->
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
<!-- 编写查询主键的sql语句 -->
select EMPLOYEES_SEQ.nextval from dual
</selectKey>
<!-- 插入时的主键是从序列中获得的 -->
insert into tbl_employee (id,last_name,email,gender) values (#{id},#{lastName},#{email},#{gender})
</insert>
在上面的例子中,首先会运行selectKey标签中的语句,并设置Employee的id,然后才会调用插入语句。这样就实现了数据库自动生成主键类似的行为。
四、参数处理
1. 单个参数:mybatis不会做任何处理,即参数名可以任意写。
语法:#{参数名}(参数名可以任意写,因为只有只有一个参数值)
作用:取出参数值
示例:在sql映射文件中,修改传入的单个参数值-------------单个参数时,参数名可以任意写
2. 多个参数:mybatis会做特殊处理,多个参数会被封装成map,需要从map中取值
说明:map中的key的取值是param1,param2......(或者参数的索引值)。map中的value的取值是传入的参数值。
语法1:#{param1},#{param2}......
语法2:#{0},#{1}......
作用:取出多个参数值
示例:在sql映射文件中,传入多个参数
在sql映射文件对应的接口中,定义多个参数的查询方法
在sql映射文件中,使用语法1或语法2完成查询
注意:
若在sql映射文件中直接使用参数名获取参数,会出现异常
异常:Error querying database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [0, 1, param1, param2]
解决方法:使用上述语法1或语法2
3. 命名参数---------推荐使用!!!
背景:当mybatis中有多个参数时,使用上述方法获取参数时不能见名知意,为此,使用命名参数,明确指定封装参数时map中的key值。
使用:key值使用@Param注解指定的值,最后使用#{参数名}即可获取到参数值
示例:mybatis中有多个参数时,仍然想通过参数名获取参数值,而不是param1,param2....或者参数索引值的方式
第一步:在接口中,使用@Param注解指定参数名
package com.csu.marden;
import org.apache.ibatis.annotations.Param;
public interface EmployeeMapper {
//根据id和lastName两个字段查询
public Employee getEmpByIdAndLastName(@Param("id")Integer id, @Param("lastName")String lastName);
//根据id查询数据,并封装成Employee对象
public Employee getEmpById(Integer id);
//添加一个Employee对象
public void addEmp(Employee employee);
//修改一个Employee对象
public void updateEmp(Employee employee);
//根据id,删除一个Employee对象
public void deleteEmpById(Integer id);
}
第二步:在sql映射文件中,通过上述指定的参数名获取参数值
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.csu.marden.EmployeeMapper">
<!-- 多个参数查询方法 -->
<select id="getEmpByIdAndLastName" resultType="employee">
select * from tbl_employee where id = #{id} and last_name=#{lastName}
</select>
<!-- 单个参数查询方法 -->
<select id="getEmpById" resultType="employee" >
select * from tbl_employee where id = #{id}
</select>
<!--插入方法 -->
<insert id="addEmp" parameterType="employee" useGeneratedKeys="true" keyProperty="id" >
insert into tbl_employee (last_name,email,gender) values (#{lastName},#{email},#{gender})
</insert>
<!-- 更新方法 -->
<update id="updateEmp" parameterType="employee" >
update tbl_employee set last_name=#{lastName},email=#{email},gender=#{gender} where id=#{id}
</update>
<!-- 删除方法 -->
<delete id="deleteEmpById" parameterType="integer">
delete from tbl_employee where id=#{id}
</delete>
</mapper>
4. 多个参数使用POJO(将整个对象作为参数)
背景:当指定多个参数时,由于参数数目较多,处理较复杂。当多个参数正好是业务逻辑的数据模型,可以直接传入POJO
示例:
第一步:在接口中,使用@Param注解指定参数名(为传入的POJO对象指定参数名)
package com.csu.marden;
import org.apache.ibatis.annotations.Param;
public interface EmployeeMapper {
//根据id和lastName两个字段查询
这里一定要加这个注解,不然在配置文件中会找不到这个对象
public Employee getEmpByIdAndLastName(@Param("emp")Employee employee);
//根据id查询数据,并封装成Employee对象
public Employee getEmpById(Integer id);
//添加一个Employee对象
public void addEmp(Employee employee);
//修改一个Employee对象
public void updateEmp(Employee employee);
//根据id,删除一个Employee对象
public void deleteEmpById(Integer id);
}
第二步:在sql映射文件中,通过上述指定的参数名获取参数值
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.csu.marden.EmployeeMapper">
<!-- 多个参数查询方法 -->
<select id="getEmpByIdAndLastName" resultType="employee">
select * from tbl_employee where id = #{emp.id} and last_name=#{emp.lastName}
</select>
<!-- 单个参数查询方法 -->
<select id="getEmpById" resultType="employee" >
select * from tbl_employee where id = #{id}
</select>
<!--插入方法 -->
<insert id="addEmp" parameterType="employee" useGeneratedKeys="true" keyProperty="id" >
insert into tbl_employee (last_name,email,gender) values (#{lastName},#{email},#{gender})
</insert>
<!-- 更新方法 -->
<update id="updateEmp" parameterType="employee" >
update tbl_employee set last_name=#{lastName},email=#{email},gender=#{gender} where id=#{id}
</update>
<!-- 删除方法 -->
<delete id="deleteEmpById" parameterType="integer">
delete from tbl_employee where id=#{id}
</delete>
</mapper>
第三步:测试结果
package com.csu.marden;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Test {
public static void main(String[] args) throws IOException {
//1.根据xml配置文件(全局配置文件,即mybatis-config.xml)创建一个SqlSessionFactory对象
String resources="mybatis-config.xml";
InputStream inputStream=Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//2.根据SqlSessionFactory创建SqlSession,每个SqlSession代表和数据库的一次会话
SqlSession openSession=sqlSessionFactory.openSession();
try{
//3.获取接口的实现类对象(mybatis会为接口自动创建一个代理对象,代理对象会执行增删改查)
EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class);
//4.通过接口的实现类对象调用接口的查询方法
Employee employee=new Employee(9,"jerry","jerry@csu.edu.cn","0");
Employee result=mapper.getEmpByIdAndLastName(employee);
System.out.println(result);
openSession.commit();
}finally{
openSession.close();
}
}
}
5. 多个参数使用Map
背景:当指定多个参数时,由于参数数目较多,处理较复杂。当多个参数没有现成的数据模型,可以使用Map的方式来统一传入参数
语法:#{key}----------取出Map中对应的值
示例:
第一步:在接口中,定义使用Map作为参数的查询方法
package com.csu.marden;
import java.util.Map;
import org.apache.ibatis.annotations.Param;
public interface EmployeeMapper {
//根据Map查询
public Employee getEmpByMap(Map<String,Object> map);
//根据id和lastName两个字段查询
public Employee getEmpByIdAndLastName(@Param("emp")Employee employee);
//根据id查询数据,并封装成Employee对象
public Employee getEmpById(Integer id);
//添加一个Employee对象
public void addEmp(Employee employee);
//修改一个Employee对象
public void updateEmp(Employee employee);
//根据id,删除一个Employee对象
public void deleteEmpById(Integer id);
}
第二步:在sql映射文件中,根据Map中的key值,直接获取参数值
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.csu.marden.EmployeeMapper">
<!-- 多个参数使用Map传入的查询方法 -->
<select id="getEmpByMap" resultType="employee">
select * from tbl_employee where id=#{id} and last_name=#{lastName}
</select>
<!-- 多个参数查询方法 -->
<select id="getEmpByIdAndLastName" resultType="employee">
select * from tbl_employee where id = #{emp.id} and last_name=#{emp.lastName}
</select>
<!-- 单个参数查询方法 -->
<select id="getEmpById" resultType="employee" >
select * from tbl_employee where id = #{id}
</select>
<!--插入方法 -->
<insert id="addEmp" parameterType="employee" useGeneratedKeys="true" keyProperty="id" >
insert into tbl_employee (last_name,email,gender) values (#{lastName},#{email},#{gender})
</insert>
<!-- 更新方法 -->
<update id="updateEmp" parameterType="employee" >
update tbl_employee set last_name=#{lastName},email=#{email},gender=#{gender} where id=#{id}
</update>
<!-- 删除方法 -->
<delete id="deleteEmpById" parameterType="integer">
delete from tbl_employee where id=#{id}
</delete>
</mapper>
第三步:测试结果
package com.csu.marden;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Test {
public static void main(String[] args) throws IOException {
//1.根据xml配置文件(全局配置文件,即mybatis-config.xml)创建一个SqlSessionFactory对象
String resources="mybatis-config.xml";
InputStream inputStream=Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//2.根据SqlSessionFactory创建SqlSession,每个SqlSession代表和数据库的一次会话
SqlSession openSession=sqlSessionFactory.openSession();
try{
//3.获取接口的实现类对象(mybatis会为接口自动创建一个代理对象,代理对象会执行增删改查)
EmployeeMapper mapper=openSession.getMapper(EmployeeMapper.class);
//4.通过接口的实现类对象调用接口的查询方法
Map<String,Object> map=new HashMap<String,Object>();
map.put("id", 1);
map.put("lastName", "marden");
Employee result=mapper.getEmpByMap(map);
System.out.println(result);
openSession.commit();
}finally{
openSession.close();
}
}
}
五、参数封装
情形一:
public Employee getEmp(@Param("id")Integer id, String lastName);
取值:id===>#{id}或者#{param1}
lastName===>#{param2}
情形二:
public Employee getEmp(Integer id, @Param("e")Employee emp);
取值: id===>#{param1}
lastName===>#{e.lastName}或者#{param2.lastName}
注意:
- 如果是Collection(List,Set)类型或者数组类型,mybatis也会特殊处理,同样是将list,set或者数组封装在Map中。
- 在Map中,如果是Collection类型,其key值是collection。如果是List类型,其key值还可以是list。如果是Array类型,其key值还可以是array。
情形三:
public Employee getEmpById(List<Integer> ids);
取值: 取出第一个id的值===>#{list[0]}
总结:
多个参数时mybatis会将参数封装成Map,为了不混乱,可以使用@Param来指定封装时使用的key,使用#{key}就可以取出map中key对应的value值。
六、参数值的获取
方式:
- 方式一:#{}
- 方式二:&{}
区别:
- #{}:是以预编译的形式,使用占位符,将参数设置到sql语句中(PreparedStatement),放置sql注入
- ${}:取出的值直接拼装在sql语句中,会有安全问题,例如order by ${name}
- 大多情况下,取参数的值使用#{}的方式。当原生JDBC不支持占位符的地方可以使用${}的方式进行取值
- 比如分表或者排序时,按照年份分表拆表 select * from ${year} _salary where xxx; 排序时 select * from tbl_employee order by ${f_name} ${order};
- 注意:占位符并不是支持任何位置,比如表名,排序不支持占位符操作!!!
原始语句:
select * from tbl_employee where id=${id} and last_name=#{lastName}
预编译语句:
select * from tbl_employee where id=2 and last_name=?
#{}更丰富的用法:
- #{}可以规定参数的一些规则,如javaType、jdbcType、mode(存储过程)、numericScale、resultMap、typeHandler、jdbcTypeName、experssion
- jdbcType通常需要在某种特定条件下使用,例如,我们的数据为null时,有些数据库可能不能识别mybatis对null的默认处理,比如Oracle下保存null字段,会报无效的类型错误(JdbcType OTHER),因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型
- 使用:修改sql映射文件中#{email,jdbcType=NULL} 或者 修改全局配置文件中jdbcTypeForNull=NULL