1 介绍
这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper
映射文件了。我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作。
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
2 初始化工程
2.1 导包
2.2 导配置
在src或resources目录下新建jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://192.168.254.128:3306/mybatis03?characterEncoding=utf-8 jdbc.username=root jdbc.password=123456
在src或resources目录下新建log4j.properties
### 设置### log4j.rootLogger = debug,stdout ### 输出信息到控制抬 ### log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
在src或resources目录下新建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> <properties resource="jdbc.properties"></properties> <settings> <setting name="logImpl" value="log4j"/> </settings> <typeAliases> <!--如果类比较多,可以指定包,通过扫包的方式给包中的所有类起别名--> <package name="cn.oldlu.domain"/> </typeAliases> <!--数据源环境--> <environments default="developement"> <environment id="developement"> <transactionManager type="JDBC"></transactionManager> <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> </configuration>
3 使用注解增删改查
3.1 环境准备
3.1.1 建库建表
CREATE DATABASE `mybatis03`; USE `mybatis03`; DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, `age` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; insert into `student`(`id`,`name`,`age`) values (1,'张三',23),(2,'李四',24),(3,'王五',25);
3.1.2 创建Student实体类
在cn.oldlu.domain包下创建Student类
package cn.oldlu.domain; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Student { private Integer id; private String name; private Integer age; }
3.1.3 创建StudentDao接口
在cn.oldlu.dao包下创建StudentDao接口
package cn.oldlu.dao; public interface StudentDao { }
3.1.4 配置映射关系
在mybatis-config.xml中配置映射关系,因为现在映射关系通过注解体现,所以应该扫描包含注解的接口
<mappers> <!--扫描dao包下的所有接口中的注解--> <package name="cn.oldlu.dao"/> </mappers>
3.2 查询演示
第一步:在StudentDao接口中添加查询方法
List<Student> findAll();
第二步:在查询方法上添加@select注解
@Select("select * from student") List<Student> findAll();
第三步:编写测试方法
@Test public void testFindAll()throws Exception{ /*1.读取配置文件*/ InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); /*2.解析配置文件*/ SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); /*3.获取操作数据库的对象*/ SqlSession sqlSession = sqlSessionFactory.openSession(); /*4.获取代理对象*/ StudentDao studentDao = sqlSession.getMapper(StudentDao.class); /*5.调用代理对象的方法完成查询*/ List<Student> students = studentDao.findAll(); /*6.释放资源*/ sqlSession.close(); //测试查询结果 System.out.println(students); }
形参中有多个值,在SQL中怎么获取?
//@Select("select * from student where id = #{arg0} and name = #{arg1}") //@Select("select * from student where id = #{param1} and name = #{param2}") @Select("select * from student where id = #{ID} and name = #{NAME}") Student findByIdAndName(@Param("ID") Integer id, @Param("NAME") String name);//使用@Param给形参起名字,在SQL中使用#{名字} 获取形参的值
3.3 新增演示
第一步:在StudentDao接口中添加新增方法
int insert(Student student);
第二步:在新增方法上添加@Insert注解
/*#{name},#{age}对应的是形参Student对象的属性的名字,表示把name,age属性的值取出来*/ @Insert("insert into student(name,age) values(#{name},#{age})") int insert(Student student);
第三步:编写测试方法,注意默认需要手动提交事务,必须手动commit
@Test public void testInsert()throws Exception{ /*1.读取配置文件*/ InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); /*2.解析配置文件*/ SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); /*3.获取操作数据库的对象*/ SqlSession sqlSession = sqlSessionFactory.openSession(); /*4.获取代理对象*/ StudentDao studentDao = sqlSession.getMapper(StudentDao.class); /*5.调用代理对象的方法完成新增*/ Student stu = new Student(null, "李克勤", 33); int count = studentDao.insert(stu); sqlSession.commit(); /*6.释放资源*/ sqlSession.close(); //测试查询结果 System.out.println("共"+count+"行记录受到影响"); }
3.4 修改演示
第一步:在StudentDao接口中添加更新方法
int update(Student student);
第二步:在更新方法上添加@Update注解
/*#{name},#{age},#{id}对应的是形参Student对象的属性的名字,表示把name,age,id属性的值取出来*/ @Update("update student set name=#{name},age=#{age} where id=#{id}") int update(Student student);
第三步:编写测试方法,注意默认需要手动提交事务,必须手动commit
@Test public void testUpdate()throws Exception{ /*1.读取配置文件*/ InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); /*2.解析配置文件*/ SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); /*3.获取操作数据库的对象*/ SqlSession sqlSession = sqlSessionFactory.openSession(); /*4.获取代理对象*/ StudentDao studentDao = sqlSession.getMapper(StudentDao.class); /*5.调用代理对象的方法完成更新*/ Student stu = new Student(6, "李克清", 33); int count = studentDao.update(stu); sqlSession.commit(); /*6.释放资源*/ sqlSession.close(); //测试查询结果 System.out.println("共"+count+"行记录受到影响"); }
3.5 删除演示
第一步:在StudentDao接口中添加删除方法
int delete(Integer id);
第二步:在删除方法上添加@Delte注解
/*#{id}对应的是形参Student对象的属性的名字,表示把id属性的值取出来*/ @Delete("delete from student where id = #{id}") int delete(Integer id);
第三步:编写测试方法,注意默认需要手动提交事务,必须手动commit
@Test public void testDelete()throws Exception{ /*1.读取配置文件*/ InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); /*2.解析配置文件*/ SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); /*3.获取操作数据库的对象*/ SqlSession sqlSession = sqlSessionFactory.openSession(); /*4.获取代理对象*/ StudentDao studentDao = sqlSession.getMapper(StudentDao.class); /*5.调用代理对象的方法完成删除*/ int count = studentDao.delete(1); sqlSession.commit(); /*6.释放资源*/ sqlSession.close(); //测试查询结果 System.out.println("共"+count+"行记录受到影响"); }
3.6 #{}与${}的区别
以下两种写法的区别
@Insert("insert into student (name,age) values('${name}',${age})") @Insert("insert into student (name,age) values(#{name},#{age})")
$底层使用Statement,最终会拼接出一个完整的SQL, insert into student (name,age) values(‘严朝阳’,12)
#底层使用PrepareStatement ,最终拼接的SQL带占位符insert into student (name,age) values(?,?)
PrepareStatement 效率高,防止SQL注入,书写更加方便。
4 SQL功能类
4.1 介绍
我们在使用注解开发的时候有一些不足
- sql语句中的关键字可能不小心写错。
- 不方便执行动态SQL语句。
那么SQL功能类是个不错的选择。
使用方式分为2步
- 第一步:使用SQL对象生成要执行的sql语句
- 第二步:使用@SelectProvider替换之前的@Select,使用@InsertProvider替换之前的@Insert….
4.2 环境搭建
4.2.1 创建表
CREATE TABLE `user`( uid INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(100) NOT NULL, PASSWORD VARCHAR(50) NOT NULL, email VARCHAR(50), birthday DATE ); INSERT INTO `user` (NAME,PASSWORD,email,birthday) VALUES("尼古拉斯赵四",'123456','nglszs@qq.com','1888-09-02'); INSERT INTO `user` (NAME,PASSWORD,email,birthday) VALUES("尼古拉斯赵五",'123456','nglszw@qq.com','1889-09-03'); INSERT INTO `user` (NAME,PASSWORD,email,birthday) VALUES("尼古拉斯赵六",'123456','nglszl@qq.com','1890-09-04'); INSERT INTO `user` (NAME,PASSWORD,email,birthday) VALUES("尼古拉斯赵七",'123456','nglszq@qq.com','1891-09-05');
4.2.2 创建实体类
package cn.oldlu.domain; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class User { private Integer uid; private String name; private String password; private String email; private String birthday;//数据库中是Date类型,java中可以是Date,也可以是String类型 }
4.2.3 定义UserSQL功能类
在cn.oldlu.sql 包下新建UserSQL功能类
package cn.oldlu.sql; import org.apache.ibatis.jdbc.SQL; public class UserSQL { }
4.2.4 定义UserDao接口
在cn.oldlu.dao包下新建UserDao接口
package cn.oldlu.dao; import cn.oldlu.domain.User; import cn.oldlu.sql.UserSQL; import org.apache.ibatis.annotations.SelectProvider; import java.util.List; public interface UserDao { }
4.3 查询@SelectProvider
4.3.1 普通查询
1.在UserSQL功能类中添加生成SQL语句的方法
/**功能:生成查询全部的SQL*/ public String findAll(){ return new SQL(){ { SELECT("*"); FROM("`user`"); } }.toString(); }
2.在UserDao接口中添加findAll方法
@SelectProvider(type = UserSQL.class, method = "findAll") List<User> findAll();
3.测试方法
@Test public void testFindAll()throws Exception{ /*1.读取配置文件*/ InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); /*2.解析配置文件*/ SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); /*3.获取操作数据库的对象*/ SqlSession sqlSession = sqlSessionFactory.openSession(); /*4.获取代理对象*/ UserDao userDao = sqlSession.getMapper(UserDao.class); /*5.调用代理对象的方法完成查询*/ List<User> users = userDao.findAll(); /*6.释放资源*/ sqlSession.close(); //测试查询结果 System.out.println(users); }
4.3.2 动态SQL查询
1.在UserSQL功能类中添加生成SQL语句的方法
/**功能:根据传入的条件动态拼接查询SQL*/ /*#{}表示获取形参User对象的属性值,所以#{}中写的是属性名*/ public String findByCondition(User user){ return new SQL(){ { SELECT("*"); FROM("`user`"); if(user!=null){ if (user.getUid() != null){ WHERE("uid = #{uid}"); } if (user.getName() != null){ WHERE("name = #{name}"); } if (user.getPassword() != null){ WHERE("password = #{password}"); } if (user.getEmail() != null){ WHERE("email = #{email}"); } if (user.getBirthday() != null){ WHERE("birthday = #{birthday}"); } } } }.toString(); }
2.在UserDao接口中添加findAll方法
@SelectProvider(type = UserSQL.class,method = "findByCondition") List<User> findByCondition(User user);
3.测试方法
@Test public void testFindByCondition()throws Exception{ /*1.读取配置文件*/ InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); /*2.解析配置文件*/ SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); /*3.获取操作数据库的对象*/ SqlSession sqlSession = sqlSessionFactory.openSession(); /*4.获取代理对象*/ UserDao userDao = sqlSession.getMapper(UserDao.class); /*5.调用代理对象的方法完成查询*/ User user = new User(); user.setUid(1); List<User> users = userDao.findByCondition(user); /*6.释放资源*/ sqlSession.close(); //测试查询结果 System.out.println(users); }
4.4 新增@InsertProvider
1.在UserSQL功能类中添加生成SQL语句的方法
/**功能:插入*/ /*#{}表示获取形参User对象的属性值,所以#{}中写的是属性名*/ public String insert(User user){ return new SQL(){ { INSERT_INTO("`user`"); VALUES("name,password,email,birthday","#{name},#{password},#{email},#{birthday}"); } }.toString(); }
2.在UserDao接口中添加insert方法
@InsertProvider(type = UserSQL.class,method = "insert") int insert(User user);
3.测试方法
@Test public void testInsert()throws Exception{ /*1.读取配置文件*/ InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); /*2.解析配置文件*/ SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); /*3.获取操作数据库的对象*/ SqlSession sqlSession = sqlSessionFactory.openSession(); /*4.获取代理对象*/ UserDao userDao = sqlSession.getMapper(UserDao.class); /*5.调用代理对象的方法完成查询*/ User user = new User(); user.setName("猪悟能"); user.setPassword("123456"); user.setBirthday("1800-09-09"); user.setEmail("zhuwuneng@qq.com"); int count = userDao.insert(user); /*6.提交事物*/ sqlSession.commit(); /*6.释放资源*/ sqlSession.close(); //测试查询结果 System.out.println("共"+count+"行受到影响"); }
4.5 更新@UpdateProvider
1.在UserSQL功能类中添加生成SQL语句的方法
/**生成根据ID更新的SQL*/ /*#{}表示获取形参User对象的属性值,所以#{}中写的是属性名*/ public String update(User user) { return new SQL() { { UPDATE("`user`"); if (user.getName() != null) { SET("name = #{name}"); } if (user.getPassword() != null) { SET("password = #{password}"); } if (user.getEmail() != null) { SET("email = #{email}"); } if (user.getBirthday() != null) { SET("birthday = #{birthday}"); } WHERE("uid=#{uid}"); } }.toString(); }
2.在UserDao接口中添加update方法
@UpdateProvider(type = UserSQL.class,method = "update") int update(User user);
3.测试方法
@Test public void testUpdate()throws Exception{ /*1.读取配置文件*/ InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); /*2.解析配置文件*/ SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); /*3.获取操作数据库的对象*/ SqlSession sqlSession = sqlSessionFactory.openSession(); /*4.获取代理对象*/ UserDao userDao = sqlSession.getMapper(UserDao.class); /*5.调用代理对象的方法完成查询*/ User user = new User(); user.setUid(1); user.setName("猪悟能"); user.setEmail("zhuwuneng@qq.com"); int count = userDao.update(user); /*6.提交事物*/ sqlSession.commit(); /*6.释放资源*/ sqlSession.close(); //测试查询结果 System.out.println("共"+count+"行受到影响"); }
4.6 删除@DeleteProvider
1.在UserSQL功能类中添加生成SQL语句的方法
/**生成根据uid删除的SQL语句*/ /*#{uid}表示获取形参Integer的值,名字任意*/ public String deleteByUid(Integer uid){ return new SQL(){ { DELETE_FROM("`user`"); WHERE("uid = #{uid}"); } }.toString(); }
2.在UserDao接口中添加deleteByUid方法
@DeleteProvider(type = UserSQL.class, method = "deleteByUid") int deleteByUid(Integer uid);
3.测试方法
@Test public void testDelete()throws Exception{ /*1.读取配置文件*/ InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); /*2.解析配置文件*/ SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); /*3.获取操作数据库的对象*/ SqlSession sqlSession = sqlSessionFactory.openSession(); /*4.获取代理对象*/ UserDao userDao = sqlSession.getMapper(UserDao.class); /*5.调用代理对象的方法完成查询*/ int count = userDao.deleteByUid(1); /*6.提交事物*/ sqlSession.commit(); /*6.释放资源*/ sqlSession.close(); //测试查询结果 System.out.println("共"+count+"行受到影响"); }
4.7 常见错误
Error querying database. Cause:
org.apache.ibatis.binding.BindingException: Parameter ‘pageIndex’ not
found. Available parameters are [arg1, arg0, param1, param2] Cause:
org.apache.ibatis.binding.BindingException: Parameter ‘pageIndex’ not
found. Available parameters are [arg1, arg0, param1, param2]
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:149) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140) at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:147) at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:80) at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:57) at com.sun.proxy.$Proxy2.selectByPage(Unknown Source) at com.mycom.myapp.courseselectionsystem.mapper.CourseMapperTest.testSelectByPage(CourseMapperTest.java:40) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) at org.junit.runners.ParentRunner.run(ParentRunner.java:309) at org.junit.runner.JUnitCore.run(JUnitCore.java:160) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.apache.ibatis.binding.BindingException: Parameter ‘pageIndex’ not found. Available parameters are [arg1, arg0, param1, param2]
at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:212)
at org.apache.ibatis.reflection.wrapper.MapWrapper.get(MapWrapper.java:45)
at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:122)
at org.apache.ibatis.executor.BaseExecutor.createCacheKey(BaseExecutor.java:219)
at org.apache.ibatis.executor.CachingExecutor.createCacheKey(CachingExecutor.java:146)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:82)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:147)
… 28 more
错误代码
List selectByPage(int pageIndex, int pageSize);
原因
mybatis在Mapper接口使用注解开发时,传递参数时需要在前面添加@Param注解。
解决
在参数前面添加@Param注解。
注意,是要在dao层添加,即SSM项目的Mapper接口类中。
正确代码
List selectByPage(@Param(“pageIndex”) int pageIndex, @Param(“pageSize”) int pageSize);
格式即:@Param(参数名)