MyBatis 实践

From: https://www.cnblogs.com/luyiba/p/6303717.html

MyBatis简介

MyBatis前身是iBatis,是一个基于Java的数据持久层/对象关系映射(ORM)框架.

MyBatis是对JDBC的封装,使开发人员只需关注SQL本身,而不需花费过多的精力去处理如注册驱动设置参数创建Connection/Statement解析结果集等JDBC过程性代码.MyBatis基于XML/注解的方式配置Statement,执行SQL,并将执行结果映射成Java对象, 大大降低了数据库开发的难度.

MyBatis is a first class persistence framework with support for custom SQL, stored procedures and advanced mappings. MyBatis eliminates almost all of the JDBC code and manual setting of parameters and retrieval of results. MyBatis can use simple XML or Annotations for configuration and map primitives, Map interfaces and Java POJOs (Plain Old Java Objects) to database records.
– MyBatis项目地址/在线文档.


初识MyBatis

使用MyBatis需要在pom.xml中添加如下依赖:

<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.3.0</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.36</version>
</dependency>

Select

  • 配置mybatis/mybatis-configuration.xml
    作为MyBatis的全局配置文件,其配置了MyBatis的运行环境信息(如数据源/mapper文件等).
<?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"><!-- 配置JDBC事务管理--><transactionManager type="JDBC"/><!-- 配置数据源--><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://host:port/db?characterEncoding=utf-8"/><property name="username" value="username"/><property name="password" value="password"/></dataSource></environment></environments><!-- 加载mapper映射文件 --><mappers><mapper resource="mybatis/mapper/UserDAO.xml"/></mappers>
</configuration>
  • 书写UserDAO(mapper映射)
    最为MyBatis最核心的部分,配置了操作数据库的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="namespace"><select id="selectUserById" parameterType="java.lang.Integer" resultType="com.fq.domain.User">SELECT * FROM user WHERE id = #{id};</select><select id="selectUserByName" parameterType="java.lang.String" resultType="com.fq.domain.User">SELECT * FROM user WHERE name LIKE '%${value}%';</select></mapper>
属性描述
namespace命名空间,用于隔离SQL语句
parameterType定义SQL输入映射类型,MyBatis通过OGNL从输入对象中获取参数传入SQL语句.
resultType定义SQL输出映射类型,MyBatis将SQL查询结果的一行记录映射为resultType指定的类型.

mapper映射文件名有UserDAO.xml/UserMapper.xml/User.xml等几种形式, 其一般存放在与mybatis-configuration.xml同级的mapper目录下,由于其主要作用为定义SQL语句与映射关系, 因此一般统称为mapper映射文件.

  • 定义PO类
    PO类主要作用为SQL(输入/输出)映射,通常与数据库表对应:
/*** @author jifang* @since 15/12/31 下午2:27.*/
public class User {private Integer id;private String name;private String password;public User() {}public User(Integer id, String name, String password) {this.id = id;this.name = name;this.password = password;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +", password='" + password + '\'' +'}';}
}
  • UserDAO(Java对象)
    获得SqlSession,执行SQL语句, 得到映射结果:
/*** @author jifang* @since 16/2/24 下午6:15.*/
public class UserDAO {private SqlSessionFactory factory;@Beforepublic void setUp() throws IOException {String resource = "mybatis/mybatis-configuration.xml";factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(resource));}@Testpublic void selectUserById() {try (SqlSession session = factory.openSession()) {User user = session.selectOne("namespace.selectUserById", 1);System.out.println(user);}}@Testpublic void selectUserByName() {try (SqlSession session = factory.openSession()) {List<User> users = session.selectList("namespace.selectUserByName", "student");for (User user : users) {System.out.println(user);}}}
}

Insert

  • mapper
<insert id="insertUser" parameterType="com.fq.domain.User">INSERT INTO user(name, password) VALUES(#{name}, #{password});
</insert>
  • UserDAO
@Test
public void insertUser() {try (SqlSession session = factory.openSession()) {User user = new User();user.setName("new_name1");user.setPassword("new_password");session.insert("namespace.insertUser", user);session.commit();}
}

自增主键返回

修改mapper文件,添加<selectKey/>,可以将MySQL的自增主键(即刚刚插入数据时生成的ID)返回:

<insert id="insertUser" parameterType="com.fq.domain.User"><selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">SELECT LAST_INSERT_ID();</selectKey>INSERT INTO user(name, password) VALUES(#{name}, #{password});
</insert>
属性描述
keyProperty指定存储到DO中的哪个属性;
orderselectKey执行顺序(相对于insert语句),AFTER/BEFORE;
resultType主键返回类型(DO中对应属性的类型);
LAST_INSERT_ID()MySQL函数,返回auto_increment自增列新记录值.
  • UserDAO
@Test
public void insertUser() {try (SqlSession session = factory.openSession()) {System.out.println(session);User user = new User(null, "new_name", "new_password");session.insert("namespace.insertUser", user);// 需要在commit之后才能获得自增主键session.commit();System.out.println(user.getId());}
}

该功能还可以通过<insert/>useGeneratedKeys/keyProperty两个属性合作完成, 详见MyBatis文档.


Update

  • mapper
<update id="updateUserById" parameterType="com.fq.domain.User">UPDATE user SET name = #{name}, password = #{password} WHERE id = #{id};
</update>
  • UserDAO
@Test
public void updateUserById() {try (SqlSession session = factory.openSession(true)) {session.update("namespace.updateUserById",new User(1, "feiqing", "ICy5YqxZB1uWSwcVLSNLcA=="));}
}

Delete

  • mapper
<delete id="deleteUserById" parameterType="java.lang.Integer">DELETE FROM user WHERE id = #{id};
</delete>
  • UserDAO
@Test
public void deleteUserById() {try (SqlSession session = factory.openSession(true)) {session.delete("namespace.deleteUserById", 51615);}
}

小结

  • #{}/${}

    • #{}: 表示一个占位符号,实现向PreparedStatement占位符中设置值(#{}表示一个占位符?),自动进行Java类型到JDBC类型的转换(因此#{}可以有效防止SQL注入).#{}可以接收简单类型或PO属性值,如果parameterType传输的是单个简单类型值,#{}花括号中可以是value或其它名称.
    • ${}: 表示拼接SQL串,通过${}可将parameterType内容拼接在SQL中而不进行JDBC类型转换,${}可以接收简单类型或PO属性值,如果parameterType传输的是单个简单类型值,${}花括号中只能是value.
      虽然${}不能防止SQL注入,但有时${}会非常方便(如order by排序,需要将列名通过参数传入SQL,则用ORDER BY ${column},使用#{}则无法实现此功能(详见JDBC基础关于PreparedStatement的讨论).
  • SqlSession
    提供操作数据库的方法(如:selectOne/selectList).但SqlSession是线程不安全的,因此最好将其定义成局部变量使用.

  • MyBatis优点(与JDBC相比)
    • SQL写在Java代码中导致不易维护, 而MyBatis将SQL写在mapper中,XML与Java代码分离.
    • 向SQL语句传参繁琐(如:SQL的where条件不一,SQL数据类型与Java不同),MyBatis通过parameterType自动将Java对象映射至SQL语句.
    • 结果集解析麻烦(SQL变化导致解析代码变化,SQL数据类型与Java不同),MyBatis通过resultType自动将SQL执行结果映射成Java对象.

附: 最好在pom.xml中添加一个日志系统实现(logback/log4j), 这样会在调试程序时打印日志信息,便于查错, 以logback为例:

  • pom.xml
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.1.2</version>
</dependency>
  • logback.xml
<configuration><property name="logRoot" value="/data/logs"/><property name="pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{0} - %msg%n"/><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${pattern}</pattern></encoder></appender><appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${logRoot}/common-server.%d{yyyy-MM-dd}.log</fileNamePattern><maxHistory>7</maxHistory></rollingPolicy><encoder><pattern>${pattern}</pattern></encoder></appender><root level="DEBUG"><appender-ref ref="STDOUT"/><appender-ref ref="FILE"/></root></configuration>

其他关于MyBatis日志的详细信息可参考MyBatis文档日志部分.


DAO开发

使用MyBatis开发DAO有两个方法,原始DAO开发Mapper映射DAO开发.


原始DAO开发

原始DAO开发需要开发人员编写DAO接口DAO实现,如根据ID查询用户信息:

  • mapper(同前)
<select id="selectUserById" parameterType="java.lang.Integer" resultType="com.fq.domain.User">SELECT * FROM user WHERE id = #{id};
</select>
  • UserDAO接口
/*** @author jifang* @since 16/2/22 上午10:20.*/
public interface UserDAO {User selectUserById(Integer id) throws Exception;
}
  • UserDAO实现
public class UserDAOImpl implements UserDAO {private SqlSessionFactory factory;public UserDAOImpl(SqlSessionFactory factory) {this.factory = factory;}@Overridepublic User selectUserById(Integer id) throws Exception {SqlSession session = factory.openSession();User user = session.selectOne("namespace.selectUserById", id);session.close();return user;}
}
  • Client
public class MyBatisClient {@Testpublic void originalClient() throws Exception {UserDAO dao = new UserDAOImpl(new SqlSessionFactoryBuilder().build(ClassLoader.getSystemResourceAsStream("mybatis/mybatis-configuration.xml")));User user = dao.selectUserById(1);System.out.println(user);}
}
  • 原始DAO开发中存在的问题:
    1) DAO实现方法体中存在很多过程性代码.
    2) 调用SqlSession的方法(select/insert/update)需要指定Statement的id,存在硬编码,不利于代码维护.

Mapper映射开发

mapper映射开发方法只需编写DAO接口,MyBatis根据接口定义与mapper文件中的SQL语句动态创建接口实现.

  • mapper
<?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.fq.mybatis.UserDAO"><select id="selectUserById" parameterType="java.lang.Integer" resultType="com.fq.domain.User">SELECT * FROM user WHERE id = #{id};</select>
</mapper>

注意: 此时namespace必须与UserDAO接口的全限定名相同.

  • UserDAO接口与前面相同, 但不再使用UserDAOImpl
  • Client
/*** @author jifang* @since 16/2/22 下午2:57.*/
public class MyBatisClient {private SqlSession session;private SqlSessionFactory factory;@Beforepublic void setUp() {factory = new SqlSessionFactoryBuilder().build(ClassLoader.getSystemResourceAsStream("mybatis/mybatis-configuration.xml"));session = factory.openSession();}@Testpublic void mapperClient() throws Exception {UserDAO dao = session.getMapper(UserDAO.class);User user = dao.selectUserById(1);System.out.println(user);}@Afterpublic void tearDown() {session.close();}
}
  • mapper映射开发方法需要遵循以下规范:
    1. mapper文件中的namespace与DAO接口的全限定名相同;
    2. mapper文件中的Statement的id与DAO接口方法名相同;
    3. mapper文件中的StatementparameterType/resultType与DAO方法的入参/回参类型相同.

Mapper映射

mapper映射文件(如UserDAO.xml)主要作用是定义SQL语句(每个SQL是一个Statement),是MyBatis的核心.

MyBatis官方推荐使用mapper映射的方法来开发DAO,因此我们以后就不再过多介绍原始DAO的开发.


输入映射

多个形参

传递简单类型前面示例已经使用过,在此就不再赘述.当需要传递多个形参时,不再需要设置parameterType参数:

  • mapper
<update id="updateUserById">UPDATE user SET name = #{1}, password = #{2} WHERE id = #{0};
</update>
  • UserDAO
void updateUserById(Integer id, String name, String password) throws Exception;

传入PO

MyBatis使用OGNL表达式解析对象属性值:

  • mapper
<select id="selectUserByNamePassword" parameterType="com.fq.domain.User" resultType="com.fq.domain.User">SELECT *FROM userWHERE name = #{name} AND password = #{password};
</select>
  • UserDAO
User selectUserByNamePassword(User user) throws Exception;

传入Map

  • mapper
<select id="selectUserByMap" parameterType="java.util.Map" resultType="com.fq.domain.User">SELECT *FROM userWHERE name = #{name} AND password = #{password};
</select>

#{}花括号内对应Mapkey.

  • UserDAO
User selectUserByMap(Map<String, Object> map) throws Exception;

输出映射

输出简单类型

  • mapper
<select id="selectUserCount" parameterType="java.lang.String" resultType="java.lang.Integer">SELECT count(*)FROM userWHERE name LIKE '%${value}%';
</select>
  • UserDAO
Integer selectUserCount(String name) throws Exception;

返回简单类型必须保证查询结果只有一行记录,最终将第一个字段的值转换为输出类型.


输出PO对象/列表

  • 前面已经演示过输出两种类型(selectUserById/selectUserByName虽然当时使用的是原始DAO开发方法, 但mapper定义形式大同小异),因此在这儿只做简单总结:
    1. 输出单个PO对象和输出PO列表在mapper中定义的resultType是一样的;
    2. 输出单个PO对象要保证SQL查询结果为单条数据,其内部使用selectOne方法调用;
    3. 输出PO列表表示查询结果可能为多条,其内部使用selectList方法调用,接口返回值可用List<PO>/Set<PO>承载.

输出Map

输出PO对象完全可以改用Map输出,字段名作key,字段值作value.

  • mapper
<select id="selectUserLikeName" resultType="java.util.Map">SELECT *FROM userWHERE name LIKE '%${value}%';
</select>
  • UserDAO
List<Map<String, Object>> selectUserLikeName(String name) throws Exception;

resultMap

resultType可将查询结果映射为PO,但前提是PO属性名SQL字段名必须一致,如不一致,则可通过resultMap作对应映射:

  • mapper
<resultMap id="userMap" type="com.fq.domain.User"><id column="user_id" property="id"/><result column="user_name" property="name"/><result column="user_password" property="password"/>
</resultMap><select id="selectUserByName" parameterType="java.lang.String" resultMap="userMap">SELECTid       user_id,name     user_name,password user_passwordFROM userWHERE name = #{name};
</select>
属性描述
<id/>表示查询结果集的唯一标识;
<result/>表示普通结果,即PO属性;
column表示SQL查询出来的字段名,
property表示PO属性.
  • UserDAO接口同前.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402634.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

皮克斯背后新技术

2019独角兽企业重金招聘Python工程师标准>>> 皮克斯每创作一部动画作品总会给观众带来或多或少的惊喜&#xff0c;而很多影视动画从业人员也习惯于关注他们的每一部作品的制作&#xff0c;因为新技术往往就在这些制作过程中诞生&#xff0c;如今皮克斯动画已经成为C…

数据结构:二叉查找树(C语言实现)

数据结构&#xff1a;二叉查找树 二叉查找树 基础知识 关于二叉树的基础知识&#xff0c;请看我的一篇博客:二叉树的链式存储 二叉查找树的特征 二叉查找树或者是一棵空树&#xff0c;或者是具有下列性质的二叉树&#xff1a;  1.若其左子树不空&#xff0c;则左子树上所有结…

Effective Java 电子书 apk版本下载

下载安装包以后&#xff0c;安装即可阅读该书了&#xff0c;并且实时展示每章节代码哦&#xff0c;并且可以运行哦&#xff0c;赶快下载体验吧. Effective Java中文第二版下载地址&#xff1a;下载 应用截图&#xff1a; 转载于:https://www.cnblogs.com/spring87/p/6090010.ht…

给你的Mr.Right画张择偶地图像

爱一个人就算做不到爱他的全部&#xff0c;至少也应该尊重他的真实&#xff0c;而不是苛求他变成你想要的样子。 娶妻当娶郭芙蓉&#xff0c;经典语录。我是郭芙蓉&#xff0c;我不会武功&#xff0c;我来自江湖&#xff0c;我与众不同。再苦再累&#xff0c;就当自己是二百五&…

java实现HTTP请求的三种方式

From: https://www.cnblogs.com/hhhshct/p/8523697.html 目前JAVA实现HTTP请求的方法用的最多的有两种&#xff1a;一种是通过HTTPClient这种第三方的开源框架去实现。HTTPClient对HTTP的封装性比较不错&#xff0c;通过它基本上能够满足我们大部分的需求&#xff0c;HttpClien…

SpringBoot时间戳与MySql数据库记录相差14小时排错

From: http://www.cnblogs.com/jason1990/archive/2018/11/28/10032181.html 项目中遇到存储的时间戳与真实时间相差14小时的现象,以下为解决步骤. 问题 CREATE TABLE incident (id int(11) NOT NULL AUTO_INCREMENT,created_time timestamp NOT NULL DEFAULT CURRENT_TIMES…

mysql重置root密码方法

2019独角兽企业重金招聘Python工程师标准>>> 1. 先关闭mysqld 2. 运行&#xff1a; mysqld_safe --skip-grant-tables 3. 另开一个窗口&#xff0c;用 mysql -uroot 登录mysql&#xff0c;执行 UPDATE mysql.user SET PasswordPASSWORD(你的密码) WHERE User…

插入排序之C++实现

描述 插入排序是一种简单直观的排序算法。它的基本思想是将一个待排序的数据序列分为已排序和未排序两部分&#xff0c;每次从未排序序列中取出一个元素&#xff0c;然后将它插入到已排序序列的适当位置&#xff0c;直到所有元素都插入完毕&#xff0c;即完成排序。 实现思路…

Spring在Java Filter注入Bean为Null的问题解决

From: https://www.cnblogs.com/EasonJim/p/7666009.html 在Spring的自动注入中普通的POJO类都可以使用Autowired进行自动注入&#xff0c;但是除了两类&#xff1a;Filter和Servlet无法使用自动注入属性。&#xff08;因为这两个归Web容器管理&#xff09;可以用init&#xf…

Mybatis:resultMap的使用总结

From: https://www.cnblogs.com/kenhome/p/7764398.html Mybatis的介绍以及使用&#xff1a;http://www.mybatis.org/mybatis-3/zh/index.html resultMap是Mybatis最强大的元素&#xff0c;它可以将查询到的复杂数据&#xff08;比如查询到几个表中数据&#xff09;映射到一个…

MyBatis总结六:resultMap详解(包含多表查询)

From: https://www.cnblogs.com/Alex-zqzy/p/9296039.html 简介&#xff1a;   MyBatis的每一个查询映射的返回类型都是ResultMap&#xff0c;只是当我们提供的返回类型属性是resultType的时候&#xff0c;MyBatis对自动的给我们把对应的值赋给resultType所指定对象的属性&a…

基于beego一键创建RESTFul应用

2019独角兽企业重金招聘Python工程师标准>>> API应用开发入门 Go是非常适合用来开发API应用的&#xff0c;而且我认为也是Go相对于其他动态语言的最大优势应用。beego在开发API应用方面提供了非常强大和快速的工具&#xff0c;方便用户快速的建立API应用原型&#…

MyBatis中in的使用

From: https://www.cnblogs.com/w-bb/articles/6378031.html foreach的主要用在构建in条件中&#xff0c;它可以在SQL语句中进行迭代一个集合。 foreach元素的属性主要有 item&#xff0c;index&#xff0c;collection&#xff0c;open&#xff0c;separator&#xff0c;close…

MyBatis总结五:#{}和${}的用法和区别

From: https://www.cnblogs.com/blazeZzz/p/9295634.html #{}的用法&#xff1a; 我们发现&#xff0c;在Mapper.xml映射文件中&#xff0c;经常使用#{属性名} 来作为SQL语句的占位符&#xff0c;来映射Sql需要的实际参数 如果只有一个参数 <select id"getUserById&…

iOS多视图代码操作

2019独角兽企业重金招聘Python工程师标准>>> 摘抄 http://supershll.blog.163.com/blog/static/370704362012112021115/ 2.1.1 层次结构 基于树的层次结构对iPhone屏幕上的可见内容进行排序。从主窗口开始&#xff0c;视图以特殊的分层方式布置。所有视图都可以有…

(转)Tiny210v2( S5PV210 ) 平台下 FIMD 对应 的 framebuffer 驱动中,关于 video buffer 的理解...

原文:http://www.arm9home.net/read.php?tid-25938.html 管理提醒&#xff1a; 本帖被 xoom 执行加亮操作(2012-12-13)如之前所说&#xff0c;一直想知道显示数据都在哪个地方&#xff0c;通常的数据&#xff0c;比如 framebuffer 中的显示数据&#xff0c;和OpenGL 处理的数据…

C链表反转(时间复杂度O(n))

面试的时候经常会出现的问题,现在都做一遍,回忆一下,练练手. 这个题目需要注意两点: 1.head->next 要先设置为NULL ,否则反转后,它还是指向之前的next节点 2.需要有一个tmp指针,临时保存p->next的地址,这个在改变一个节点的next地址时,经常会用到 示意图 代码实现 #inclu…

mysql中find_in_set()函数的使用及in()用法详解

From: http://www.manongjc.com/article/2710.html MySQL手册中find_in_set函数的语法解释&#xff1a; FIND_IN_SET(str,strlist) str 要查询的字符串 strlist 字段名 参数以”,”分隔 如 (1,2,6,8,10,22) 查询字段(strlist)中包含(str)的结果&#xff0c;返回结果为null…

浅谈SQLiteOpenHelper之onUpgrade例子

当你看到这个博文&#xff0c;首先你要了解onCreate这个创建方法&#xff0c;再来继续下文!&#xff08;可以参考我的上一个博文http://www.cnblogs.com/896240130Master/p/6119616.html&#xff09; 这个onUpgrade类要在onCreate类的基础上建立&#xff01;我们知道onUpgrade是…

MySQL 字符串分割 SUBSTRING_INDEX函数

From: MySQL 字符串分割 SUBSTRING_INDEX函数 Sql代码 SUBSTRING_INDEX(str,delim,count) 用delim 分割str&#xff0c;取第count个子串 url http://www.medhelp.org/forums/Acne/show/56 Java代码 substring_index(url,"/",1) 结果是http: Java代码 substri…