系统学习知识请认准 知识追寻者(同公众号),错过作者,你有可能要走好多弯路 经过第一篇的入门文章,小白们都对mybatis的搭建流程应该都很熟悉,这篇文章主讲的是如何使用mybatis实现数据库的增删改查,以及相关的标签属性、配置说明,可以说这篇文章是为你以后的学习和工作打下坚实基础文章,小白们要认真看,认真敲,下面是准备的sql语句。此文强到没朋友!!!!
CREATE TABLE `course` ( `courseName` varchar(255) DEFAULT NULL COMMENT '课程名称', `coursePrice` decimal(10,2) DEFAULT NULL COMMENT '课程价格', `courseId` int(11) NOT NULL AUTO_INCREMENT COMMENT '课程id', PRIMARY KEY (`courseId`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='课程'; INSERT INTO `mybatis`.`course`(`courseName`, `coursePrice`, `courseId`) VALUES ('java基础课', 500.00, 1); INSERT INTO `mybatis`.`course`(`courseName`, `coursePrice`, `courseId`) VALUES ('javaWeb课程', 1000.00, 2); 复制代码
引入的依赖跟上篇文章一致,这次我们会用到junit单元测试。
<dependencies> <!-- mybatis support--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!-- log4j support --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- junit support --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- mysql support --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>6.0.5</version> </dependency> </dependencies> 复制代码
log4j.properties 放在 resource目录下,修改包名并配置日志级别为debug,用于打印sql;
log4j.rootLogger = info,stdout log4j.appender.stdout = org.apache.log4j.ConsoleAppender ## 注意改薄 log4j.logger.com.zszxz.mybatis.config.mapper = debug 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 复制代码
mybatis中经常使用到实体就是可以用作封装参数或者对返回的结果集进行映射到实体;
/** * @Author lsc * <p> </p> */ public class Course { // 课程名称 private String courseName; // 课程价格 private double coursePrice; // 主键 private Long courseId; public String getCourseName() { return courseName; } public void setCourseName(String courseName) { this.courseName = courseName; } public double getCoursePrice() { return coursePrice; } public void setCoursePrice(double coursePrice) { this.coursePrice = coursePrice; } public Long getCourseId() { return courseId; } public void setCourseId(Long courseId) { this.courseId = courseId; } } 复制代码
mybatis-config.xml 存放于resource目录下,用于配置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"> <!-- mybaits配置 --> <configuration> <!-- 全局环境配置--> <environments default="development"> <environment id="development"> <!-- 事物 --> <transactionManager type="JDBC"/> <!-- 配置数据源 --> <dataSource type="POOLED"> <!-- 数据库驱动 5.6以上版本使用com.mysql.cj.jdbc.Driver --> <property name="driver" value="com.mysql.jdbc.Driver"/> <!-- 数据库路径 --> <property name="url" value="jdbc:mysql://192.168.0.105:3306/mybatis"/> <!-- 账号--> <property name="username" value="root"/> <!--密码 --> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!-- 引入自定义mapper.xml 在resource 目录下 --> <mappers> <mapper resource="configMapper/CourseMapper.xml"/> </mappers> </configuration> 复制代码
这么注意命名空间与接口的关系,方法名与id的关系,返回值是类的全限定名;其中CourseMapper.xml文件是放在resource目录下configMapper中;
CourseMapper.xml
<?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.zszxz.mybatis.config.mapper.CourseMapper"> <select id="getCourseList" resultType="com.zszxz.mybatis.config.entity.Course"> SELECT * FROM course </select> </mapper> 复制代码
mapper接口
public interface CourseMapper { /* * * @Author lsc * <p> 查询课程列表</p> * @Param [] * @Return java.util.List<com.zszxz.mybatis.config.entity.Course> */ List<Course> getCourseList(); } 复制代码
这边的@before注解是在每次执行测试类之前都会执行,固后面的查询测试将直接列出测试方法,不会重复@before注解下的方法;
@RunWith(JUnit4.class) public class SelectTest { SqlSession sqlSession = null; // @Before 会在执行测试类之前执行该方法 @Before public void before() throws IOException { // 资源路径 resource目录下 String resource = "mybatis-config.xml"; // 配置mybatis获得输入流 InputStream inputStream = Resources.getResourceAsStream(resource); // 创建 SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //从 SqlSessionFactory 中获取 SqlSession sqlSession= sqlSessionFactory.openSession(); } /* * * @Author lsc * <p> 查询课程列表</p> * @Param [] * @Return void */ @Test public void testSelect(){ // 使用传入方法名查询形式 List<Course> getCourseList = sqlSession.selectList("getCourseList"); for (Course course : getCourseList){ System.out.println(course.getCourseName()); } sqlSession.close(); } } 复制代码
==> Preparing: SELECT * FROM course [DEBUG] 2019-12-05 21:02:00,238 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ==> Parameters: [DEBUG] 2019-12-05 21:02:00,256 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) <== Total: 2 java基础课 javaWeb课程 复制代码
这种方式是主流方式,你不可能每次查询都输入一个方法名吧,很不现实,我们更倾向于面向对象方式直接调用方法获得想要的结果;
测试结果和上次一致;
@Test public void testSelect2(){ // 获得mapper的形式 CourseMapper courseMapper = sqlSession.getMapper(CourseMapper.class); // 遍历打印课程 for (Course course : courseMapper.getCourseList()){ System.out.println(course.getCourseName()); } sqlSession.close(); } 复制代码
属性 | 说明 |
---|---|
id | 在命名空间中唯一的标识符,可以代表引用该sql |
parameterType | 入参的完全限定名或者别名,可选操作,默认未设置。 |
resultType | 返回值的期望类型的类的完全限定名或者别名,可以使用 resultType 或 resultMap,但不能同时使用 |
resultMap | 外部结果集映射配置,可以与实体字段映射;可以使用 resultType 或 resultMap,但不能同时使用 |
flushCache | 设置true会清口本地缓存和二级缓存,默认是false |
useCache | 设置为true会导致查询结果集保存至二级缓存,对于select默认是true |
timeout | 抛出异常前,驱动程序等待的最大毫秒数,默认未设置,依赖于驱动 |
fetchSize | 驱动程序每次批量返回行数,默认未设置 |
statementType | 设置STATEMENT,PREPARED 或 CALLABLE 。分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED |
resultSetType | 设置结果集类型,FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset)默认为设置 DEFAULT |
databaseId | 如果配置了数据库厂商标识(databaseIdProvider),会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
resultOrdered | 嵌套查询语句中使用,设置为true,则sql执行结果为嵌套结果或者分组 |
resultSets | 在多个结果集情况下,会列出结果集名称,以逗号分隔 |
/* * * @Author lsc * <p> 普通新增</p> * @Param [course] * @Return int */ int addCourse(Course course); 复制代码
<insert id="addCourse" parameterType="com.zszxz.mybatis.config.entity.Course"> INSERT INTO course ( courseName, coursePrice ) VALUES ( #{ courseName },#{ coursePrice }); </insert> 复制代码
@Test public void testInsert(){ // 创建入参实体 Course Course course = new Course(); course.setCourseName("知识追寻者的心理课"); course.setCoursePrice(10000); // 获得mapper CourseMapper courseMapper = sqlSession.getMapper(CourseMapper.class); // 调用添加课程方法 courseMapper.addCourse(course); // 事物提交 sqlSession.commit(); // 关闭会话 sqlSession.close(); } 复制代码
可以看见我们用#{}在xml中达到了原生jdbc预编译后的结果每次进入都是以?占位符形式,有效防止sql注入问题;${}通常用在获取字段名称,表名,而非入参,否则会有sql注入风险。
[DEBUG] 2019-12-05 21:58:44,193 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ==> Preparing: INSERT INTO course ( courseName, coursePrice ) VALUES ( ?,?); [DEBUG] 2019-12-05 21:58:44,221 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ==> Parameters: 知识追寻者的心理课(String), 10000.0(Double) [DEBUG] 2019-12-05 21:58:44,224 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) <== Updates: 1 复制代码
/* * * @Author lsc * <p> 新增数据并获得主键方式1</p> * @Param [course] * @Return int */ int addCourseAndGetIdbyGen(Course course); 复制代码
在 <insert>
标签中 相比于 <select>
标签多出了3个属性。
useGeneratedKeys keyProperty keyColumn
<insert id="addCourseAndGetIdbyGen" parameterType="com.zszxz.mybatis.config.entity.Course" useGeneratedKeys="true" keyProperty="courseId" keyColumn="courseId"> INSERT INTO course ( courseName, coursePrice ) VALUES ( #{ courseName },#{ coursePrice }); </insert> 复制代码
小白们这里注意啊,获得插入的主键是从我们入参的Course实体属性中获得;
@Test public void testInsertGetId1(){ Course course = new Course(); course.setCourseName("知识追寻者的课程1"); course.setCoursePrice(100000); CourseMapper courseMapper = sqlSession.getMapper(CourseMapper.class); courseMapper.addCourseAndGetIdbyGen(course); // System.out.println("返回的课程id: "+course.getCourseId()); sqlSession.commit(); sqlSession.close(); } 复制代码
[DEBUG] 2019-12-05 22:17:01,788 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ==> Preparing: INSERT INTO course ( courseName, coursePrice ) VALUES ( ?,?); [DEBUG] 2019-12-05 22:17:01,820 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ==> Parameters: 知识追寻者的课程1(String), 100000.0(Double) [DEBUG] 2019-12-05 22:17:01,822 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) <== Updates: 1 返回的课程id: 4 复制代码
/* * * @Author lsc * <p> 新增数据并获得主键方式2</p> * @Param [course] * @Return int */ int addCourseAndGetIdbyKey(Course course); 复制代码
这里使用了 <selectKey>
标签做返回插入数据的主键,这种方式使用于具有主键自增的数据库管理系统,也使用于像oracle这样不具有主键自增的数据库管理系统,而且每种数据库管理系统支持的 <selectKey>
标签内容都不一样,但都大同小异,读者可以查阅数据库管理系统相关文档使用。需要注意的是 order 属性
,由于mysql这种主键自增型数据库管理系统是语句插入后才能获得主键,固使用 after;如果是 oracle这种非自增型主键数据库管理系统,是语句插入之前主键就以及存在,固要设置属性为before;
<insert id="addCourseAndGetIdbyKey" parameterType="com.zszxz.mybatis.config.entity.Course"> INSERT INTO course ( courseName, coursePrice, courseId ) VALUES ( #{ courseName },#{ coursePrice },#{ courseId }); <selectKey resultType="long" order="AFTER" keyProperty="courseId"> SELECT LAST_INSERT_ID(); </selectKey> </insert> 复制代码
代码很清秀,不用作者多余的注释了吧。
@Test public void testInsertGetId2(){ Course course = new Course(); course.setCourseName("知识追寻者的课程2"); course.setCoursePrice(100000); CourseMapper courseMapper = sqlSession.getMapper(CourseMapper.class); courseMapper.addCourseAndGetIdbyGen(course); // System.out.println("返回的课程id: "+course.getCourseId()); sqlSession.commit(); sqlSession.close(); } 复制代码
[DEBUG] 2019-12-05 22:25:31,232 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ==> Preparing: INSERT INTO course ( courseName, coursePrice ) VALUES ( ?,?); [DEBUG] 2019-12-05 22:25:31,258 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ==> Parameters: 知识追寻者的课程2(String), 100000.0(Double) [DEBUG] 2019-12-05 22:25:31,261 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) <== Updates: 1 返回的课程id: 5 复制代码
更新相比于查询和插入,其相对简单;
/* * * @Author lsc * <p> </p> * @Param [course] * @Return int */ int updateCourse(Course course); 复制代码
<update id="updateCourse" parameterType="com.zszxz.mybatis.config.entity.Course"> UPDATE course set courseName = #{courseName}, coursePrice = #{coursePrice} WHERE courseId = #{courseId} </update> 复制代码
@Test public void testUpdate2(){ Course course = new Course(); course.setCourseName("知识追寻者的课程3"); course.setCoursePrice(1000); course.setCourseId(5L); CourseMapper courseMapper = sqlSession.getMapper(CourseMapper.class); courseMapper.updateCourse(course); sqlSession.commit(); sqlSession.close(); } 复制代码
[DEBUG] 2019-12-05 22:38:46,093 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ==> Preparing: UPDATE course set courseName = ?, coursePrice = ? WHERE courseId = ? [DEBUG] 2019-12-05 22:38:46,121 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ==> Parameters: 知识追寻者的课程3(String), 1000.0(Double), 5(Long) [DEBUG] 2019-12-05 22:38:46,124 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) <== Updates: 1 复制代码
删除操作和更新操作类似,没什么技巧难度;
/* * * @Author lsc * <p> </p> * @Param [] * @Return int */ int deleteCourse(Long courseId); 复制代码
<delete id="deleteCourse" parameterType="long"> DELETE FROM course WHERE courseId = #{courseId} </delete> 复制代码
@Test public void testDelte(){ CourseMapper courseMapper = sqlSession.getMapper(CourseMapper.class); courseMapper.deleteCourse(1L); sqlSession.commit(); sqlSession.close(); } 复制代码
[DEBUG] 2019-12-05 22:39:29,926 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ==> Preparing: DELETE FROM course WHERE courseId = ? [DEBUG] 2019-12-05 22:39:29,952 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ==> Parameters: 1(Long) [DEBUG] 2019-12-05 22:39:29,955 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) <== Updates: 1 复制代码