搭建Mybatis讲解及问题整理
初出茅庐
大家好,第一次用这种方式来记录,这篇文章是我第一次搭建Mybatis框架的亲身经历,期间发生的问题让我哭笑不得。一个小小的错误可能就会花费你很长时间,时间宝贵话不多说了下面就是我本次搭建的项目的步骤及遇到问题的解决办法,欢迎指点不足!
一、配置jdbc连接数据库及问题讲解
1.准备数据库新建表,执行mysql语句插入数据。
-- ----------------------------
-- Table structure for `orders`
-- ----------------------------
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '下单用户id',
`number` varchar(32) NOT NULL COMMENT '订单号',
`createtime` datetime NOT NULL COMMENT '创建订单时间',
`note` varchar(100) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
KEY `FK_orders_1` (`user_id`),
CONSTRAINT `FK_order_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of orders
-- ----------------------------
INSERT INTO `orders` VALUES ('3', '1', '12345', '2015-02-04 13:22:35', null);
INSERT INTO `orders` VALUES ('4', '1', '11123', '2015-02-03 13:22:41', null);
INSERT INTO `orders` VALUES ('5', '10', '33324', '2015-02-12 16:13:23', null);
-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` date DEFAULT NULL COMMENT '生日',
`sex` char(1) DEFAULT NULL COMMENT '性别',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', '黄忠', null, '2', null);
INSERT INTO `user` VALUES ('2', '吕布', '2014-07-10', '1', '北京市');
INSERT INTO `user` VALUES ('3', '蔡文姬', null, '1', '辽宁');
INSERT INTO `user` VALUES ('4', '孙尚香', null, '1', '沈阳');
INSERT INTO `user` VALUES ('5', '后羿', null, '1', '抚顺');
INSERT INTO `user` VALUES ('6', '甄姬', null, '1', '吉林');
INSERT INTO `user` VALUES ('7', '虞姬', null, null, null);
2.创建工程
(先空着,等项目搭建完成补充)
3.配置pom.xml文件,加载对数据库的依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>mybatis01</groupId>
<artifactId>mybatis01</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- mysql版本 -->
<!--<mysql.version>8.0.13</mysql.version>-->
<mysql.version>8.0.13</mysql.version>
<!-- junit版本 -->
<junit.version>4.12</junit.version>
</properties>
<dependencies>
<!-- mysql数据库依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- junit依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
4.编写链接数据库代码。
package com.java.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class JdbcTest {
public static void main(String[] args) {
Connection con = null;
PreparedStatement psmt = null;
ResultSet set = null;
try {
// 1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.创建数据库的连接对象
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8",
"root","root");
// 3.定义sql语句
String sql = "select * from user where id = ?";
// 4.创建statement对象
psmt = con.prepareStatement(sql);
// 5.设置参数
psmt.setInt(1, 1);
// 6.执行
set = psmt.executeQuery();
// 7.处理结果集
while (set.next()) {
System.out.println("用户Id:" + set.getInt("id") + ",用户名称:" + set.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
//8.释放资源
if (set != null)set.close();
if (psmt != null)psmt.close();
if (con != null)con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
测试结果为:用户Id:1,用户名称:黄忠
5.总结jdbc链接数据库问题及解决办法
- 频繁创建数据库连接对象、释放、容易造成系统资源浪费,影响系统性能。企业项目中可以使用连接池解决这个问题,但是使用Jdbc需要自己实现连接池。mybatis框架已经提供连接池。
- sql语句定义、参数设置、结果集处理存在硬编码。企业项目中sql语句变化的可能性较大,一旦发生变化,需要修改java代码,系统需要重新编译,重新发布。不好维护。
- 结果集处理存在重复代码,处理麻烦。如果可以映射成为java对象会比较方便。
问题1:java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
解决办法:
在url链接处库名后加上?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
,目的是指定编码格式为utf8。
问题2:Loading class
com.mysql.jdbc.Driver’. This is deprecated. The new driver class iscom.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
解决办法:将com.mysql.jdbc.Driver修改为com.mysql.cj.jdbc.Driver,原因是前者是mysql-connector-java 5中的,后者是mysql-connector-java 6中的。
二、mybatis介绍
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)
当前,最新版本是MyBatis 3.5.2 ,其发布时间是2019年7月15日。
三、mybatis程序编写
实现简单功能:对表数据进行增、删、改、查等操作。
1.配置pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>mybatis01</groupId>
<artifactId>mybatis01</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- mysql版本 -->
<!--<mysql.version>8.0.13</mysql.version>-->
<mysql.version>8.0.13</mysql.version>
<!-- junit版本 -->
<junit.version>4.12</junit.version>
<!-- mybatis版本号 -->
<mybatis.version>3.4.5</mybatis.version>
<!-- log4j日志包版本 -->
<slf4j.version>1.7.7</slf4j.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<dependencies>
<!-- mysql数据库依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- junit依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- log4j日志包 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.0.0</version>
</dependency>
</dependencies>
</project>
2.sqlMapConfig.xml配置文件,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">
<configuration>
<!-- 运行环境配置 -->
<!--default属性:指定使用哪一个运行环境 -->
<environments default="development">
<!--id属性:唯一标识一个运行环境 -->
<environment id="development">
<!-- 事务管理器配置,type="JDBC":mybatis框架默认使用jdbc事务 -->
<transactionManager type="JDBC" />
<!--数据源配置,type="POOLED":mybatis框架提供的连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<!-- 加载映射文件 -->
<mappers>
<mapper resource="mybatis/UserMapper.xml"/>
</mappers>
</configuration>
3.对log4j.properties日志配置
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
4.实体类User.java
注:此处值得注意的是实体类中我重写了一个toString方法,如没有此方法在执行下面的增删改查方法结果时,输出控制台的是内存地址而不是你的实体内容。
package com.java.domain;
import java.util.Date;
public class User {
private Integer id; // 主键id
private String username; // 用户名称
private Date birthday; // 生日
private String sex; // 性别
private String address; // 地址
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString(){
return "User [id=" + id + ", username=" + username + ", birthday=" + birthday + ", sex=" + sex + ", address="
+ address + "]";
}
}
5.配置UserMapper.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="mybatis">
<!-- 新增用户 -->
<insert id="addUser" parameterType="com.java.domain.User">
insert into user (id,username,birthday,sex,address)
values (#{id},#{username},#{birthday},#{sex},#{address})
</insert>
<!-- 根据用户id删除用户 -->
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id}
</delete>
<!-- 根据用户id修改用户 -->
<update id="updateUserById" parameterType="com.java.domain.User">
update user set username=#{username},sex=#{sex} where id=#{id}
</update>
<!-- 根据id查询 -->
<select id="queryUserById" parameterType="int" resultType="com.java.domain.User">
select id,username,birthday,sex,address from user where id = #{id}
</select>
<!-- 方式一:根据用户名称模糊查询用户 -->
<select id="queryUserByName1" parameterType="string" resultType="com.java.domain.User">
select idMy,username,birthday,sex,address from `user` where username like #{username}
</select>
<!-- 方式二:根据用户名称模糊查询用户 -->
<select id="queryUserByName2" parameterType="string" resultType="com.java.domain.User">
select id,username,birthday,sex,address from `user` where username like '%${value}%'
</select>
</mapper>
6.编写MyBatisTest.java类增删改查操作数据库实现方法。
*其中打印结果就不在文章中体现了。
package com.java.mybatis;
import com.java.domain.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.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
/**
* create by: superman
* description: 增删改查公共使用的部分提取出来。
* create time:
*
* @return
*/
public class MyBatisTest {
public SqlSessionFactory getSqlSessionFactory() throws IOException {
// 1.加载核心配置文件
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2.读取配置文件的内容
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(inputStream);
return sqlSessionFactory;
}
/**
* create by: superman
* description: 添加用户
* create time:
*
* @return
*/
@Test
public void addUser() throws IOException {
// 3.使用sqlSessionFactory对象,创建SqlSession对象,开启自动提交事务
SqlSession sqlSession = this.getSqlSessionFactory().openSession(true);
// 调用方法执行
User user = new User();
user.setId(1);
user.setUsername("明世隐");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("来自大明朝");
sqlSession.insert("mybatis.addUser", user);
// 事务提交
// sqlSession.commit();
// 释放资源
sqlSession.close();
}
/**
* create by: superman
* description: 根据id删除用户
* create time:
*
* @return
*/
@Test
public void deleteUser() throws IOException {
// 创建SqlSession对象
SqlSession sqlSession = this.getSqlSessionFactory().openSession(true);
// 调用方法执行
sqlSession.delete("mybatis.deleteUser", 1);
// 释放资源
sqlSession.close();
}
/**
* create by: superman
* description: 根据id修改用户
* create time:
*
* @return
*/
@Test
public void updateUserById() throws IOException {
// 创建SqlSession对象
SqlSession sqlSession = this.getSqlSessionFactory().openSession(true);
// 调用方法执行
// 创建用户对象
User user = new User();
user.setId(2);
user.setUsername("达摩");
user.setSex("2");
sqlSession.update("mybatis.updateUserById", user);
// 释放资源
sqlSession.close();
}
/**
* create by: superman
* description: 根据id查询用户
* create time:
*
* @return
*/
@Test
public void queryUserById() throws IOException {
// 3.使用sqlSessionFactory对象,创建SqlSession对象
SqlSession sqlSession = this.getSqlSessionFactory().openSession();
// 4.使用sqlSession对象,调用方法执行
Object user = sqlSession.selectOne("mybatis.queryUserById", 4);
System.out.println(user);
// 5.释放资源
sqlSession.close();
}
/**
* create by: superman
* description: 模糊查询
* create time:
*
* @return
*/
@Test
public void queryUserByName1() throws IOException {
// 3.使用sqlSessionFactory对象,创建SqlSession对象
SqlSession sqlSession = this.getSqlSessionFactory().openSession();
// 4.使用sqlSession对象,调用方法执行
List<Object> userList = sqlSession.selectList("mybatis.queryUserByName1", "%后羿%");
for (Object object : userList) {
System.out.println(object);
}
// 5.释放资源
sqlSession.close();
}
/**
* create by: superman
* description: 模糊查询
* create time:
*
* @return
*/
@Test
public void queryUserByName2() throws IOException {
// 3.使用sqlSessionFactory对象,创建SqlSession对象
SqlSession sqlSession = this.getSqlSessionFactory().openSession();
// 4.使用sqlSession对象,调用方法执行
List<Object> userList = sqlSession.selectList("mybatis.queryUserByName2", "后");
for (Object object : userList) {
System.out.println(object);
}
// 5.释放资源
sqlSession.close();
}
}
看完上面的代码,可能会有疑问为什么查询方法中出现#,$分别代表什么含义?
四、占位符#{}与字符串拼接符${}
1.占位符#{},当参数类型传递的是java简单类型的时候,花括号中的内容可以是任意字符串。
2.字符串拼接符${},当参数传递的是java简单类型的时候,花括号中的内容只能是:value
3.sql语句中使用字符串拼接,可能引起sql注入的问题。但是mybatis框架中字符串拼接符可以放心使用。原因是mybatis是后端dao层开发,参数在前端表现层(action)和业务层(service)已经处理好。
五、mybatis与hibernate比较
Hibernate:Hibernate是当前最流行的ORM框架之一,对JDBC提供了较为完整的封装。Hibernate实现功能比较多,通过HQL操作数据库,比较简单方便,但Hibernate自动生成的sql相长,不利测试和查找原因。复杂sql时,编写比较困难,同时性能也会降低Hibernate的O/R Mapping实现了POJO 和数据库表之间的映射,以及SQL的自动生成和执行。
Mybatis:Mybatis同样也是非常流行的ORM框架,主要着力点在于 POJO 与 SQL 之间的映射关系。然后通过映射配置文件,将SQL所需的参数,以及返回的结果字段映射到指定 POJO 。还是半自动化,手动编写SQL语句,同时提供丰富的参数判断功能。sql语句较清晰,可以直接进行测试,性能也较好,操作起来非常简单,相对Hibernate“O/R”而言,Mybatis 是一种“Sql Mapping”的ORM实现。