一、MyBatis入门
1.1 认识Mybatis
MyBatis 本是Apache的一个开源项目iBatis, 2010年这个项目由Apache Software Foundation 迁移到了Google Code,且改名为MyBatis 。2013年11月迁移到GitHub。iBATIS一词来源于“internet”和“abatis”的组合。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
精简解释:MyBatis是一个半自动ORM框架,其本质是对JDBC的封装。使用MyBatis重点需要程序员编写SQL命令,不需要写一行JDBC代码
2.2 第一个MyBatis程序
第一步
创建一个Maven项目并导入对应依赖
依赖
<dependencies>
<!--mybatis核心依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
第二步
创建包结构,实体类和准备数据库表
第三步
前面两步都是准备工作,现在来编写核心的配置文件
- 在mapper包中创建DeptMapper接口和对应的DeptMapper.xml文件
//DeptMapper接口
public interface DeptMapper {
List<Dept> selectAll();
}
<?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">
<!--namespace:指定DeptMapper接口的位置-->
<mapper namespace="com.lzf.mapper.DeptMapper">
<!--id:对应DeptMapper接口中的方法名
resultType:该方法的返回值类型
-->
<!--此处编写要执行的sql语句-->
<select id="selectAll" resultType="com.lzf.pojo.Dept">
select * from dept
</select>
</mapper>
- 在resouces目录下创建mybatis-config.xml文件
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!--配置数据源,指定自己数据库的信息-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--mybatis_db:数据库名称-->
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis_db?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
<!--连接mysql的用户名和密码-->
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--mapper文件要在这里注册-->
<mappers>
<mapper resource="com/lzf/mapper/DeptMapper.xml"/>
</mappers>
</configuration>
第四步
在test包中编写测试类测试
public static void main(String[] args) throws IOException {
//1、指定mybatis核心配置文件的输入流
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//2、创建出sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3、通过sqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//4.通过sqlSession获取到mapper,执行方法拿到结果
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
List<Dept> depts = mapper.selectAll();
for (Dept dept : depts) {
System.out.println(dept);
}
//关闭sqlSession
sqlSession.close();
}
//结果打印
/*
com.lzf.pojo.Dept@27a5f880
com.lzf.pojo.Dept@1d29cf23
com.lzf.pojo.Dept@5f282abb
com.lzf.pojo.Dept@167fdd33
*/
会遇到的问题
找不到DeptMapper.xml,原因是maven在编译的时候不会把java包中的配置文件编译进去,导致在target目录中找不到文件。
两种解决办法:
-
在pom.xml中添加
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build>
-
把DeptMapper.xml文件放到resources目录中,修改了文件的位置,记得mybatis-config注册时的路径也要修改
<!--mapper文件要在这里注册--> <mappers> <mapper resource="DeptMapper.xml"/> </mappers>
二、MyBatis配置详解
2.1 XML配置(核心配置文件)
前置
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
日志
在mybatis.cfg.xml中配置MyBatis所使用的具体日志实现。如果不指定将自动搜索。可能会搜到log4j,但是如果优先搜到了其他的日志实现呢,所以还是设置为好。这一来log4j就跑不了了。
<settings>
<!--配置日志-->
<setting name="logImpl" value="LOG4J"/>
</settings>
在resources目录中创建log4j.properties文件
#定义全局日志级别调试阶段推荐debug
log4j.rootLogger=debug,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=d:/msb.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %F %p %m%n
也可以使用其他的日志 logj2
<!--log4j2依赖-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
在resources目录中创建log4j2.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
<Appenders>
<Console name="Console" target="SYSTEM_ERR">
<PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="DEBUG">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
事务
在mybatis核心配置文件中 envirment中 通过transactionManager配置事务的处理策略
JDBC 这个配置直接简单使用了 JDBC 的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围
MANAGED 这个配置几乎没做什么。它从来不提交或回滚一个连接。而它会让容器来管理事务的整个生命周期(比如 Spring 或 JEE 应用服务器的上下文) 默认情况下它会关闭连接。然而一些容器并不希望这样, 因此如果你需要从连接中停止它,将closeConnection 属性设置为 false。
mybatis本身并不做事务的处理,交给其他框架去处理事务,如spring
映射器(mappers)
mapper映射文件的文件路径导入:resource属性
<mappers>
<mapper resource="com/lzf/mapper/DeptMapper.xml"/>
</mappers>
接口的全限定名导入:class属性
<mappers>
<mapper class="com.lzf.mapper.DeptMapper"/>
</mappers>
包扫描形式加载所有的mapper映射文件:package标签
<mappers>
<package name="com.lzf.mapper"/>
</mappers>
网络资路径:url属性
<!--
本地路径写法:
file:///本地路径
-->
<mappers>
<mapper url="file:///E:\01_JAVA笔记资料\11_Project\demo\mybatis_study\src\main\java\com\lzf\mapper\DeptMapper.xml"/>
</mappers>
类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
<!--设置实体类别名-->
<typeAliases>
<typeAlias type="com.lzf.pojo.Dept" alias="dept"/>
</typeAliases>
在映射文件的resultType 返回值类型 和paramterType 上就可以使用别名了
<!--此处编写要执行的sql语句-->
<select id="selectAll" resultType="dept">
select * from dept
</select>
当实体类多时一个个为实体类设置别名就比较麻烦,可以通过包扫描为实体类设置别名
<!--包设置实体类别名,默认别名为类的首字母小写
也可以自定义别名,在类上使用注解@Alias("dept") 一般不用
-->
<typeAliases>
<package name="com.lzf.pojo"/>
</typeAliases>
属性(properties)
在之前的mybatis-config.xml文件中,数据源的配置信息,我们是直接写在配置文件里面的,这样的方式不易于修改,不合理。应该在外部定义一个文件,然后在mybatis-config.xml中引入。
在resources中创建文件jdbc.properties
jdbc_driver=com.mysql.jdbc.Driver
jdbc_url=jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
jdbc_username=root
jdbc_password=123456
mybatis-config.xml中引入并使用
<!--引入外部文件-->
<properties resource="jdbc.properties"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc_driver}"/>
<!--mybatis_db:数据库名称-->
<property name="url" value="${jdbc_url}"/>
<!--连接mysql的用户名和密码-->
<property name="username" value="${jdbc_username}"/>
<property name="password" value="${jdbc_password}"/>
</dataSource>
2.2 XML映射文件(mapper.xml)
参数传递
单个基本数据类型
#{}中可以随便写,遵循见名知意
多个基本数据类型
方式1 arg* arg0 arg1 arg2 数字是索引,从0开始
方式2 param* param1 param2 param3 数字是编号,从1开始
方式3 @Param定义别名 推荐此方法,见名知意。
单个引用数据类型
#{}中写的使用对象的属性名
map集合数据类型
#{}写键的名字
多个引用数据类型
如果用@Param定义了别名,那么就不能使用arg*.属性名,但是可以使用param*.属性名和别名.属性名
CRUD
select
<!--根据id查询
id:指定接口中的方法名
resultType:该方法的返回值类型,
如果返回的是集合,那应该设置为集合包含的类型,
而不是集合本身的类型。
parameterType:指定方法的参数类型。这个属性是可选的,
因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数
-->
<select id="findById" parameterType="int" resultType="dept">
select * from dept WHERE DEPTNO = #{deptno}
</select>
insert, update 和 delete
数据变更语句 insert,update 和 delete 的实现非常接近,增删改记得提交 事务(sqlSession.commit())
<!--增加 parameterType可以不写-->
<insert id="insert" parameterType="dept">
insert into dept values(#{deptno},#{dname},#{loc})
</insert>
<!--根据ID修改 parameterType可以不写-->
<update id="update" parameterType="dept">
update dept set DNAME = #{dname},LOC = #{loc} where DEPTNO = #{deptno}
</update>
<!--根据ID删除 parameterType可以不写-->
<delete id="delete" parameterType="int">
delete from dept where DEPTNO = #{deptno}
</delete>
模糊查询
使用concat()函数来连接参数和通配符。另外注意对于特殊字符,比如<,不能直接书写,应该使用字符实体替换。
public interface DeptMapper {
/**
* 根据name模糊查询
* @param name 模糊查询的文字
* @return Dept对象List集合
*/
List<Dept> findByName(String name);
}
<?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.lzf.mapper.DeptMapper">
<select id="findByName" resultType="dept">
select * from dept WHERE DNAME like concat('%',#{name},'%')
</select>
</mapper>
注解自增回填
有时候完成添加后需要立刻获取刚刚自增的主键,由下一个操作来使用。MyBatis提供了支持,可以非常简单的获取。
/**
* 添加一个Dept
* @param dept 要添加的dept
* @return
*/
int addDept(Dept dept);
<!--
useGeneratedKeys="true" 返回数据库帮我们生成的主键
keyProperty="deptno" 生成的主键值用我们dept对象那个属性存储
-->
<insert id="addDept" parameterType="dept" useGeneratedKeys="true" keyProperty="deptno">
insert into dept values(null,#{dname},#{loc})
</insert>
resultMap
前面已经使用MyBatis完成了CRUD操作,但只是完成了对单个数据库表的操作。这肯定是远远不够的。
在实际开发中,经常会将来自多张表的数据在一个位置显示。这个时候就需要使用resultMap手动处理数据库查询字段和封装实体类属性之间的映射关系。
-
需求:根据编号查询员工信息及所在的部门信息
实体类
public class Emp {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
// 组合一个Dept对象作为自己的属性
private Dept dept;
//提供构造器、get,set
}
接口
/**
* 根据员工编号查询员工的所有信息并携带所在的部门信息
* @param empno 要查询的员工编号
* @return Emp对象,组合了Dept对象作为属性,对部门信息进行存储
*/
Emp findEmpJoinDeptByEmpno(int empno);
Empmapper.xml
<resultMap id="empJoinDept" type="emp">
<!--设置emp本身的八个属性的映射关系-->
<id property="empno" column="empno"/>
<result property="ename" column="ename"/>
<result property="job" column="job"/>
<result property="mgr" column="mgr"/>
<result property="hiredate" column="hiredate"/>
<result property="sal" column="sal"/>
<result property="comm" column="comm"/>
<result property="deptno" column="deptno"/>
<!--
association 处理一对一
封装一对一信息关系的标签
property emp类的属性名
javaType 用哪个类的对象给属性赋值
-->
<association property="dept" javaType="dept">
<id property="deptno" column="deptno"/>
<result property="dname" column="dname"/>
<result property="loc" column="loc"/>
</association>
</resultMap>
<select id="findEmpJoinDeptByEmpno" resultMap="empJoinDept">
select * from emp e
left join dept d on e.deptno = d.deptno
where EMPNO = #{empno}
</select>
-
需求:根据部门号查询部门信息及该部门的所有员工信息
实体类
public class Dept { private Integer deptno; private String dname; private String loc; // 组合一个Emp的List集合作为属性 List<Emp> emps; //提供构造器、get,set }
接口
/** * 根据部门编号查询部门信息及该部分的所有员工信息 * @param deptno 要查询的部门编号 * @return Dept对象,内部组合了一个Emp的List属性用于封装部门的所有员工信息 */ Dept findDeptJoinEmpsByDeptno(int deptno);
DeptMapper.xml
<resultMap id="deptJoinEmps" type="dept"> <id property="deptno" column="deptno"/> <result property="dname" column="dname"/> <result property="loc" column="loc"/> <!--处理一对多关系的标签--> <collection property="emps" ofType="emp"> <id property="empno" column="empno"/> <result property="ename" column="ename"/> <result property="job" column="jbo"/> <result property="mgr" column="mgr"/> <result property="hiredate" column="hiredate"/> <result property="sal" column="sal"/> <result property="comm" column="comm"/> <result property="deptno" column="deptno"/> </collection> </resultMap> <select id="findDeptJoinEmpsByDeptno" resultMap="deptJoinEmps"> SELECT * FROM dept d LEFT JOIN emp e ON d.DEPTNO = e.DEPTNO WHERE d.DEPTNO = 30 </select>
三、动态SQL
if
接口
//条件查询
List<Emp> findByCondition(Emp emp);
映射文件
select id="findByCondition" resultType="emp">
select * from emp where 1=1
<if test="empno != null">
and empno =#{empno}
</if>
<if test="ename != null and ename != ''">
and ename like concat('%',#{ename},'%')
</if>
<if test="job != null and job != ''">
and job =#{job}
</if>
<if test="mgr != null">
and mgr =#{mgr}
</if>
<if test="hiredate != null">
and hiredate =#{hiredate}
</if>
<if test="sal != null">
and sal =#{sal}
</if>
<if test="comm != null">
and comm =#{comm}
</if>
<if test="deptno != null">
and deptno =#{deptno}
</if>
</select>
where
用于处理where关键字和and
<select id="findEmpByCondition" resultType="emp">
select * from emp
<where>
<if test="empno != null">
and empno= #{empno}
</if>
<if test="ename != null and ename != ''">
and ename= #{ename}
</if>
<if test="job != null and job != ''">
and job= #{job}
</if>
<if test="mgr != null ">
and mgr= #{mgr}
</if>
<if test="hiredate != null ">
and hiredate= #{hiredate}
</if>
<if test="sal != null">
and sal= #{sal}
</if>
<if test="comm != null ">
and comm =#{comm}
</if>
<if test="deptno != null ">
and deptno= #{deptno}
</if>
</where>
</select>
choose
前面的when条件成立 后面的 when就不再判断了
<select id="findEmpByCondition2" resultType="emp">
select * from emp
<where>
<choose>
<when test="empno != null">
and empno= #{empno}
</when>
<when test="ename != null and ename != ''">
and ename= #{ename}
</when>
<when test="job != null and job != ''">
and job= #{job}
</when>
<when test="mgr != null ">
and mgr= #{mgr}
</when>
<when test="hiredate != null ">
and hiredate= #{hiredate}
</when>
<when test="sal != null">
and sal= #{sal}
</when>
<when test="comm != null ">
and comm =#{comm}
</when>
<when test="deptno != null ">
and deptno= #{deptno}
</when>
</choose>
</where>
</select>
set
接口
//根据条件更新
int updateEmpByCondtion(Emp emp);
映射文件
<update id="updateEmpByCondtion">
update emp
<set>
<if test="ename != null and ename != '' ">
, ename =#{ename}
</if>
<if test="job != null and ename != '' ">
, job =#{job}
</if>
<if test="mgr != null ">
, mgr =#{mgr}
</if>
<if test="hiredate != null ">
, hiredate =#{hiredate}
</if>
<if test="sal != null ">
, sal =#{sal}
</if>
<if test="comm != null ">
, comm =#{comm}
</if>
<if test="deptno != null ">
, deptno =#{deptno}
</if>
</set>
where empno =#{empno}
</update>
trim
Trim 标签处理 set
<update id="updateEmpByCondition2" >
update emp
<!--prefix 要增加什么前缀
prefixOverrides 要去除什么前缀
suffix 要增加什么后缀
suffixOverrides 要去除什么后缀
set 是trim的一种特殊情况
-->
<trim prefix="set" suffixOverrides="," >
<if test="ename != null and ename != ''">
ename= #{ename},
</if>
<if test="job != null and job != ''">
job= #{job},
</if>
<if test="mgr != null ">
mgr= #{mgr},
</if>
<if test="hiredate != null ">
hiredate= #{hiredate},
</if>
<if test="sal != null">
sal= #{sal},
</if>
<if test="comm != null ">
comm =#{comm},
</if>
<if test="deptno != null ">
deptno= #{deptno},
</if>
</trim>
where empno = #{empno}
</update>
Trim标签 处理where
<select id="findEmpByCondition" resultType="emp">
select * from emp
<trim prefix="where" prefixOverrides="and">
<if test="empno != null">
and empno= #{empno}
</if>
<if test="ename != null and ename != ''">
and ename= #{ename}
</if>
<if test="job != null and job != ''">
and job= #{job}
</if>
<if test="mgr != null ">
and mgr= #{mgr}
</if>
<if test="hiredate != null ">
and hiredate= #{hiredate}
</if>
<if test="sal != null">
and sal= #{sal}
</if>
<if test="comm != null ">
and comm =#{comm}
</if>
<if test="deptno != null ">
and deptno= #{deptno}
</if>
</trim>
</select>
bind
一般用于处理模糊查询的模板
<select id= "findEmpByEname" resultType = "emp">
<bind name = "likePatten" value = " '%' + param1 + '%' "
select * from emp where ename like #{likePatten}
</select>
sql
定义公共的SQL,以便复用
<sql id="empColumn">empno,ename,job,mgr,hiredate,sal,comm,deptno</sql>
<sql id="baseSelect">select <include refid="empColumn"></include> from emp</sql>
foreach
<!--List<Emp> findByEmpnos1(int[] empnos);
collection="" 遍历的集合或者是数组
参数是数组,collection中名字指定为array
参数是List集合,collection中名字指定为list
separator="" 多个元素取出的时候 用什么文字分隔
open="" 以什么开头
close="" 以什么结尾
item="" 中间变量名
for(Person per:PersonList)
-->
<select id="findByEmpnos1" resultType="emp">
select * from emp where empno in
<foreach collection="array" separator="," open="(" close=")" item="deptno">
#{deptno}
</foreach>
</select>
<!-- List<Emp> findByEmpnos2(List<Integer> empnos);-->
<select id="findByEmpnos2" resultType="emp">
select * from emp where empno in
<foreach collection="list" separator="," open="(" close=")" item="deptno">
#{deptno}
</foreach>
</select>
四、缓存【了解】
一般不用MyBatis的缓存,用第三方缓存。
一级缓存
一级存储是SqlSession上的缓存,默认开启,是一种内存型缓存,不要求实体类对象实现Serializable接口。
测试代码
@Test
public void testFindDeptByDetpno() {
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.findByEmpno(7521);
System.out.println(emp);
// 中间发生了增删改或者是调用了SqlSession调用了commit,会自动清空缓存
sqlSession.commit();// 增删改的时候调用
EmpMapper mapper2 = sqlSession.getMapper(EmpMapper.class);
Emp emp2 = mapper2.findByEmpno(7521);
System.out.println(emp2);
System.out.println(emp==emp2);
System.out.println(mapper==mapper2);
}
二级缓存
级缓存是以namespace为标记的缓存,可以是由一个SqlSessionFactory创建的SqlSession之间共享缓存数据。默认并不开启。下面的代码中创建了两个SqlSession,执行相同的SQL语句,尝试让第二个SqlSession使用第一个SqlSession查询后缓存的数据。要求实体类必须实现序列化接口
开启二级缓存
- 全局开关:在sqlMapConfig.xml文件中的 settings 标签配置开启二级缓存
<settings>
<!--cacheEnabled的默认值就是true,所以这步的设置可以省略-->
<setting name="cacheEnabled" value="true"/>
</settings>
- 分开关:在要开启二级缓存的mapper文件中开启缓存
<mapper namespace="com.msb.mapper.EmployeeMapper">
<cache/>
</mapper>
五、注解开发【了解】
简单的且稳定不改动的SQL语句可以用注解
使用注解没有实现Java代码和SQL语句的解耦
无法实现SQL语句的动态拼接
进行多表的查询时定制ResultMap比较麻烦
public interface DeptMapper {
@Select("select * from dept where deptno =#{deptno}")
Dept findByDeptno(int deptno);
@Update("update dept set dname =#{dname}, loc =#{loc} where deptno =#{deptno}")
int updateDept(Dept dept);
@Insert("insert into dept values(DEFAULT,#{dname},#{loc})")
int addDept(Dept dept);
@Delete("delete from dept where deptno =#{deptno}")
int removeDept(int deptno);
}
qlSession使用第一个SqlSession查询后缓存的数据。要求实体类必须实现序列化接口
[外链图片转存中…(img-n05jKS7E-1631097962432)]
开启二级缓存
- 全局开关:在sqlMapConfig.xml文件中的 settings 标签配置开启二级缓存
<settings>
<!--cacheEnabled的默认值就是true,所以这步的设置可以省略-->
<setting name="cacheEnabled" value="true"/>
</settings>
- 分开关:在要开启二级缓存的mapper文件中开启缓存
<mapper namespace="com.msb.mapper.EmployeeMapper">
<cache/>
</mapper>
五、注解开发【了解】
简单的且稳定不改动的SQL语句可以用注解
使用注解没有实现Java代码和SQL语句的解耦
无法实现SQL语句的动态拼接
进行多表的查询时定制ResultMap比较麻烦
public interface DeptMapper {
@Select("select * from dept where deptno =#{deptno}")
Dept findByDeptno(int deptno);
@Update("update dept set dname =#{dname}, loc =#{loc} where deptno =#{deptno}")
int updateDept(Dept dept);
@Insert("insert into dept values(DEFAULT,#{dname},#{loc})")
int addDept(Dept dept);
@Delete("delete from dept where deptno =#{deptno}")
int removeDept(int deptno);
}