最近找了一些Mybatis的视频来学习,光看不练假把式,以这篇文章来巩固一下学到的知识,如有不足,请多多指教。
Mybatis 官方网站:http://blog.mybatis.org/
Mybatis 版本:mybatis-3.4.6
软件: IntelliJ IDEA 2018.2
JDK: jdk1.8.0_181
数据库:MySQL 5.7.20
连接工具:Navicat
源码地址:https://github.com/cyikns/myBatisStudy.git
打开官方网站会自动跳转到MyBatis博客,打开之后会看到如下这样的页面。点击页面右侧的Mybatis for java下GitHub Project。
打开链接后,跳转到GitHub,在此附上链接 https://github.com/mybatis,选择Mybatis-3。
进入页面后,拖动到页面最底端,选择 DownLoad Latest。
进入Mybatis下载页面,当前最新版本是3.4.6,在这里我就用最新版的来做测试。将mybatis-3.4.6.zip文件下载到电脑相应位置,Source code 是源码,ZIP对应windows系统,tar.gz对应linux系统,按需下载,至此mybatis下载完毕。
具体操作参考我上一篇微博。
https://blog.csdn.net/m0_38131049/article/details/81457856
数据如下图:
tb_user
tb_order
本例采用Maven来进行架构,使用Maven 3.5.4版本,在pom文件中添加依赖
4.0.0
cn.xyikns
myBatis
1.0-SNAPSHOT
org.mybatis
mybatis
3.4.6
org.slf4j
slf4j-api
1.7.25
log4j
log4j
1.2.17
commons-logging
commons-logging
1.2
mysql
mysql-connector-java
5.1.38
org.apache.maven.plugins
maven-compiler-plugin
3.7.0
1.8
UTF-8
加入配置文件
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
### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
#log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.D.File = E://logs/log.log
#log4j.appender.D.Append = true
#log4j.appender.D.Threshold = DEBUG
#log4j.appender.D.layout = org.apache.log4j.PatternLayout
#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 输出ERROR 级别以上的日志到=E://logs/error.log ###
#log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.E.File =E://logs/error.log
#log4j.appender.E.Append = true
#log4j.appender.E.Threshold = ERROR
#log4j.appender.E.layout = org.apache.log4j.PatternLayout
#log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
sqlMapConfig.xml
创建POJO
USER
package cn.cyikns.pojo;
import java.io.Serializable;
/**
* @author cyikns
* @create 2018-08-07 18:49
*/
public class User implements Serializable {
private int id;
private String username;
private String password;
private String address;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
Order
package cn.cyikns.pojo;
import java.io.Serializable;
/**
* @author cyikns
* @create 2018-08-07 19:53
*/
public class Order implements Serializable {
private int id;
private int userId;
private String shoppingName;
private int status;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getShoppingName() {
return shoppingName;
}
public void setShoppingName(String shoppingName) {
this.shoppingName = shoppingName;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
}
在config下的sqlmap目录下创建sql映射文件User.xml:
得到如图所示的mybatis工程
package cn.cyikns.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 回顾JDBC连接数据库
*
* @author cyikns
* @create 2018-08-07 16:07
*/
public class JDBCReview {
public static void main(String[] args) throws SQLException {
Connection conn = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
//加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//通过驱动管理类获取数据库连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "sql_user", "Sql_passord");
//定义SQL语句 ?表示占位符
String str = "select * from tb_user where id = ?";
//获取预处理statement
statement = conn.prepareStatement(str);
// 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
statement.setInt(1, 3);
resultSet = statement.executeQuery();
while (resultSet.next()) {
System.out.println(resultSet.getString("username"));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (conn != null) conn.close();
}
}
}
这是最原始的jdbc方法(未经封装)实现的查询数据库表的操作。
得出的结果如下
会有一段WARN
Establishing SSL connection without server’s identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn’t set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to ‘false’. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
大体的意思就是:建议不要在没有服务器身份验证的情况下建立SSL连接。 根据MySQL 5.5.45 +,5.6.26 +和5.7.6+要求如果未设置显式选项,则必须默认建立SSL连接。 为了符合不使用SSL的现有应用程序,verifyServerCertificate属性设置为“false”。 您需要通过设置useSSL = false显式禁用SSL,或者设置useSSL = true并为服务器证书验证提供信任库。
JAVA类
package cn.cyikns.test;
import cn.cyikns.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* 测试类
*
* @author cyikns
* @create 2018-08-07 20:23
*/
public class SimpleTest {
//根据用户id查询用户
@Test
public void queryUserById() {
//获取资源文件
String resource = "sqlMapConfig.xml";
try {
//获得输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
//获取sessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//打开sqlSession
SqlSession sqlSession = sessionFactory.openSession();
//查询
User user = sqlSession.selectOne("queryUserById", 1);
//输出结果
System.out.println(user);
sqlSession.commit();
sqlSession.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//模糊查询
@Test
public void queryUser() {
//获取资源文件
String resource = "sqlMapConfig.xml";
try {
//获得输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
//获取sessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//打开sqlSession
SqlSession sqlSession = sessionFactory.openSession();
//查询
List users = sqlSession.selectList("queryUser", "张");
for (User user : users) {
//输出结果
System.out.println(user);
}
sqlSession.commit();
sqlSession.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//根据ID修改数据
@Test
public void updateUser() {
//获取资源文件
String resource = "sqlMapConfig.xml";
try {
//获得输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
//获取sessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//打开sqlSession
SqlSession sqlSession = sessionFactory.openSession();
//查询
User user = new User();
user.setId(3);
user.setUsername("东方不败");
user.setPassword("aabb");
user.setAddress("湖北省武汉市黄梅县一天门村");
int count = sqlSession.update("updateUserById", user);
System.out.println(count + "--->" + user);
sqlSession.commit();
sqlSession.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//根据ID删除数据
@Test
public void delUser() {
//获取资源文件
String resource = "sqlMapConfig.xml";
try {
//获得输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
//获取sessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//打开sqlSession
SqlSession sqlSession = sessionFactory.openSession();
sqlSession.delete("delUserById", 12);
sqlSession.commit();
sqlSession.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 增加一条数据
@Test
public void addOneUser() {
try {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sessionFactory.openSession();
User user = new User();
user.setUsername("东xi不败");
user.setPassword("aabb");
user.setAddress("湖北省武汉市黄梅县一天门村");
int insert = sqlSession.insert("addUser", user);
System.out.println(user);
sqlSession.commit();//mybatis不会自动提交事务,所以需要手动提交
} catch (IOException e) {
e.printStackTrace();
}
}
}
User.xml
update tb_user set username = #{username},password = #{password},address = #{address} where id = #{id}
delete from tb_user where id = #{id}
select LAST_INSERT_ID()
insert into tb_user(username, password, address) values (#{username},#{password},#{address})
小结:
1.其中#{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换。#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
表示拼接sql串,通过 表 示 拼 接 s q l 串 , 通 过 {}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, 可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值, 可 以 接 收 简 单 类 型 值 或 p o j o 属 性 值 , 如 果 p a r a m e t e r T y p e 传 输 单 个 简 单 类 型 值 , {}括号中只能是value。
2.parameterType:指定输入参数类型,mybatis通过ognl从输入对象中获取参数值拼接在sql中。
resultType:指定输出结果类型,mybatis将sql查询结果的一行记录数据映射为resultType指定类型的对象。如果有多条数据,则分别进行映射,并把对象放到容器List中
3.selectOne查询一条记录,如果使用selectOne查询多条记录则抛出异常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)
selectList可以查询一条或多条记录。
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上。
Mapper接口开发需要遵循以下规范:
1、 Mapper.xml文件中的namespace与mapper接口的类路径相同。
2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
update tb_user set username = #{username},password = #{password},address = #{address} where id = #{id}
delete from tb_user where id = #{id}
select LAST_INSERT_ID()
insert into tb_user(username, password, address) values (#{username},#{password},#{address})
package cn.cyikns.mapper;
import cn.cyikns.pojo.User;
/**
* @author cyikns
* @create 2018-08-08 10:12
*/
public interface UserMapper {
void addUser(User user);
User selectUserById(int id);
}
注意:由于将xml文件放在maven工程中,需要在pom.xml文件中添加资源文件
src/main/java
**/*.xml
否则会报一个找不到方法的错误
别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
map Map
1) 传递简单类型
使用#{任意字符}占位符,或者{value}进行sql拼接。
2) 传递POJO对象
Mybatis使用ognl表达式解析对象字段的值,#{}或者 {value}进行sql拼接。 2) 传递POJO对象 Mybatis使用ognl表达式解析对象字段的值,#{}或者 {}括号中的值为pojo属性名称。
3 传递pojo包装对象
编写QueryVo
package cn.cyikns.pojo;
import java.io.Serializable;
/**
* @author cyikns
* @create 2018-08-08 14:11
*/
public class QueryVo implements Serializable {
//包含其他的POJO
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
UserMapper.xml
UserMapper.java
测试代码
//测试输入对象是QueryVo
@Test
public void testQueryUserByQueryVo() {
try {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
QueryVo vo = new QueryVo();
User user1 = new User();
user1.setUsername("张");
vo.setUser(user1);
List users = mapper.queryUserByQueryVo(vo);
for (User user : users) {
System.out.println(user);
}
} catch (IOException e) {
e.printStackTrace();
}
}
结果
1)输出简单类型
输出简单类型必须查询出来的结果集有一条记录,最终将第一个字段的值转换为输出类型。
2)输出POJO对象
前面有
3)输出POJO列表
前面有
resultType可以指定将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。
如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系 ,resultMap实质上还需要将查询结果映射到pojo对象中。
resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。
例如:查询订单表order的所有数据
一般情况而言
返回的值userId和ShoppingName都是不正确的
此时需要使用resultMap属性,如下所示:
得到的结果就是正确的
注意:1对1时,POJO中名称与列名称一致可以忽略不写,其他情况则不能省略
UserMapper.java
package cn.cyikns.mapper;
import cn.cyikns.pojo.QueryVo;
import cn.cyikns.pojo.User;
import java.util.List;
/**
* @author cyikns
* @create 2018-08-08 10:12
*/
public interface UserMapper {
void addUser(User user);
User selectUserById(int id);
List queryUserByQueryVo(QueryVo vo);
List queryUser(User user);
}
测试方法
@Test
public void testQueryUserByUser() {
try {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = new User();
user1.setUsername("张");
user1.setPassword("1234");
List users = mapper.queryUser(user1);
for (User user : users) {
System.out.println(user);
}
} catch (IOException e) {
e.printStackTrace();
}
}
数据库数据
SELECT * FROM
向sql传递数组或List,mybatis使用foreach解析
SELECT * FROM tb_user WHERE id IN (1,13,26)
在QueryVo中
如果直接使用List类型,那么parameterType=”list”
如果使用数组类型,那么parameterType =“java.util.Arrays”
方法一:使用ResultType
使用resultType,改造订单pojo类,此pojo类中包括了订单信息和用户信息
这样返回对象的时候,mybatis自动把用户信息也注入进来了
1)改造POJO
package cn.cyikns.pojo;
/**
* @author cyikns
* @create 2018-08-08 17:25
*/
public class OrderUser extends Order {
private String username;
private String address;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
2)编写接口
List queryAllOrders();
3)编写OrderMapper.xml
4)写测试代码
@Test
public void testQueryAllOrders() {
try {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List orders = mapper.queryAllOrders();
for (OrderUser order : orders) {
System.out.println(order);
}
} catch (IOException e) {
e.printStackTrace();
}
}
5)结果
方法二:使用ResultMap
在Order类中加入User属性,user属性中用于存储关联查询的用户信息,因为订单关联查询用户是一对一关系,所以这里使用单个User对象存储关联查询的用户信息。
1) pojo类
package cn.cyikns.pojo;
import java.io.Serializable;
/**
* @author cyikns
* @create 2018-08-07 19:53
*/
public class Order implements Serializable {
private int id;
private int userId;
private String shoppingName;
private int status;
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getShoppingName() {
return shoppingName;
}
public void setShoppingName(String shoppingName) {
this.shoppingName = shoppingName;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
@Override
public String toString() {
return "Order{" +
"id=" + id +
", userId=" + userId +
", shoppingName='" + shoppingName + '\'' +
", status=" + status +
", user=" + user +
'}';
}
}
2)写接口
List queryOrderUseResultMap1();
3)编写OrderMapper.xml
4)写测试代码
@Test
public void testQueryOrderUseResultMap() {
try {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List orders = mapper.queryOrderUseResultMap1();
for (Order order : orders) {
System.out.println(order);
}
} catch (IOException e) {
e.printStackTrace();
}
}
5)结果
1)pojo改造
package cn.cyikns.pojo;
import java.io.Serializable;
import java.util.List;
/**
* @author cyikns
* @create 2018-08-07 18:49
*/
public class User implements Serializable {
private int id;
private String username;
private String password;
private String address;
private List orders;
public List getOrders() {
return orders;
}
public void setOrders(List orders) {
this.orders = orders;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", address='" + address + '\'' +
", orders=" + orders +
'}';
}
}
2)写接口
List queryUserAndOrders();
3)编写UserMapper.xml
4)写测试
@Test
public void testQueryUserAndOrders() {
try {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List users = mapper.queryUserAndOrders();
for (User user : users) {
System.out.println(user);
}
} catch (IOException e) {
e.printStackTrace();
}
}
5)结果
写这篇日记差不多花了两天时间,刚开始不是很熟悉,慢慢的就顺畅一点了,在这里总结下,建数据库特别是主键最好是有所区分,比如User表主键用uid,Order表主键用oid,统一用id来表示很多时候会混淆到底哪个是哪个,表名字段尽量精简表意。