从JDBC到MyBatis
在学习JDBC之前,要准备好数据库信息,包括用户名密码等,下载mysql, 创建数据库,创建表,这里贴一下我创建的表信息。
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`SId` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`Sname` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`Sage` datetime(0) NULL DEFAULT NULL,
`Ssex` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `student` VALUES ('01', '赵雷', '1990-01-01 00:00:01', '男');
INSERT INTO `student` VALUES ('02', '钱电', '1990-12-21 00:20:00', '男');
INSERT INTO `student` VALUES ('03', '孙风', '1990-12-20 10:00:00', '男');
INSERT INTO `student` VALUES ('04', '李云', '1990-12-06 02:00:10', '男');
INSERT INTO `student` VALUES ('05', '周梅', '1991-12-01 03:00:20', '女');
INSERT INTO `student` VALUES ('06', '吴兰', '1992-01-01 10:00:00', '女');
INSERT INTO `student` VALUES ('07', '郑竹', '1989-01-01 20:00:00', '女');
INSERT INTO `student` VALUES ('09', '张三', '2017-12-20 10:00:00', '女');
INSERT INTO `student` VALUES ('10', '李四', '2017-12-25 20:00:00', '女');
INSERT INTO `student` VALUES ('11', '李四', '2012-06-06 00:20:00', '女');
INSERT INTO `student` VALUES ('12', '赵六', '2013-06-13 00:10:02', '女');
INSERT INTO `student` VALUES ('13', '孙七', '2014-06-01 01:20:30', '女');
- 通过JDBC操作数据库
1.1 加载数据库连接驱动
JDBC只是java提供的操作数据库的接口,不同的数据库对这个接口有不同的实现,因此,要操作相应的数据库,首先要下载数据库对应的驱动jar包,这里以mysql为例,下载mysql-connector。由于我使用的是maven,因此只需要在pom中加入mysql连接器的坐标。
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
在代码加载mysql数据库连接驱动:
Class.forName("com.mysql.jdbc.Driver");
1.2 获取数据库连接
Connection connection1 = DriverManager.getConnection(url, username, password);
1.3 准备PreparedStatement
String sql = "SELECT * FROM STUDENT WHERE SID = ?";
PreparedStatement statement2 = connection1.prepareStatement(sql);
1.4 设置参数
因为我们的sql语句里面指定了一个参数SID,所以这里要设置参数。需要注意的是,设置参数第一个参数是参数在sql语句中占位符?的位置,而且参数序号是从1开始的,也就是设置第一个参数需要如下写法,假设第一个参数是Long型的:statement.setLong(1,1L), 而不能是statement.setLong(0, 1L)。另外查询的结果集也是一样的,如果是通过列去取,也是从1开始,而不是从0开始,见1.6.
statement2.setLong(1, 1L);
1.5 执行SQL
我们要查询数据,因此,调用statement的executeQuery()方法。
ResultSet resultSet = statement2.executeQuery();
1.6 将查询结果集封装为java实体
需要注意的是,如果是以列序号的方式获取结果集中的列,序号是从1开始,也就是获取第一个列需要如下写法,假设第一个参数是Long型的:resultSet.getLong(1), 而不能是resultSet.getLong(0)。当然,更语义化的方式是通过getXxx(“column_name”)的方式更加语义化,可读性更好,也不容易出错。如:resultSet.getLong(“SID”)
while(resultSet.next()) {
// 6 查询结果集转换为VO
// String sql = "SELECT SID, SNAME, SAGE, SSEX FROM STUDENT WHERE SID = ?";
student.setId(resultSet.getLong(1));//sid是long型的,所以用结果集getLong, 但是列下标是从1开始的,所以这里不能是getLong(0); 这与PrepareStatement设置参数是一样的。
student.setName(resultSet.getString("sname"));
student.setBirthday(simpleDateFormat.format(resultSet.getTimestamp("sage")));
student.setSex(resultSet.getString("ssex"));
}
完整代码如下:
package org.jeanerk.jdbc.dao;
import java.sql.*;
import java.text.SimpleDateFormat;
public class StudentDAO {
String sql = "SELECT SID, SNAME, SAGE, SSEX FROM STUDENT WHERE SID = ?";
String url = "jdbc:mysql://127.0.0.1:3306/auth?characterEncoding=utf-8&useSSL=false";
String username = "root";
String password = "root";
public InnerStudent selectUser(Long userId) throws ClassNotFoundException {
// 1. 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2. 获取连接
Connection connection1 = null;
PreparedStatement statement2 = null;
InnerStudent student = new InnerStudent();
try {
connection1 = DriverManager.getConnection(url, username, password);
// 3. 创建PreparedStatement =
statement2 = connection1.prepareStatement(sql);
// 4. 设置参数
statement2.setLong(1, 1L);
// 5. 执行查询
ResultSet resultSet = statement2.executeQuery();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
while(resultSet.next()) {
// 6 查询结果集转换为VO
student.setId(resultSet.getLong("sid"));
student.setName(resultSet.getString("sname"));
student.setBirthday(simpleDateFormat.format(resultSet.getTimestamp("sage")));
student.setSex(resultSet.getString("ssex"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
try {
if (statement2 != null) {
statement2.close();
}
if (connection1 != null) {
connection1.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
// 用try-with-resources的写法可以将关闭连接的操作交给jvm, 减少样板代码的编写
// try (Connection connection2 = DriverManager.getConnection(url, username, password);
// PreparedStatement statement = connection2.prepareStatement(sql)) {
// statement.setLong(1, 1L);
// ResultSet resultSet = statement.executeQuery();
// SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
// while(resultSet.next()) {
// // 6 查询结果集转换为VO
// student.setId(resultSet.getLong("sid"));
// student.setName(resultSet.getString("sname"));
// student.setBirthday(simpleDateFormat.format(resultSet.getTimestamp("sage")));
// student.setSex(resultSet.getString("ssex"));
// }
// } catch (SQLException throwables) {
// throwables.printStackTrace();
// }
return student;
}
public static void main(String[] args) throws Exception {
StudentDAO userDAO = new StudentDAO();
InnerStudent student = userDAO.selectUser(1L);
System.out.println(student);
}
}
class InnerStudent {
Long id;
String name;
String birthday;
String sex;
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{id=" + id +
", name='" + name + '\'' +
", birthday='" + birthday + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
新增一条记录,删除、修改与新增差不多的逻辑,不再赘述 :
public boolean saveStudent(InnerStudent s) {
String sql = "INSERT INTO STUDENT(SID, SNAME, SAGE, SSEX) VALUES(?, ?, ?, ?)";
boolean saveResult = false;
try (Connection connection2 = DriverManager.getConnection(url, username, password);
PreparedStatement statement = connection2.prepareStatement(sql)) {
statement.setLong(1, s.id);
statement.setString(2, s.name);
statement.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
statement.setString(4, s.sex);
saveResult = statement.execute();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return saveResult;
}
- 通过MyBatis操作数据库
MyBatis最终也是通过JDBC对数据库进行操作,只不过把加载数据库连接驱动,创建数据库连接,准备PrepareStatement,将查询结果集封装为java实体的操作帮我们实现了。极大简化了我们的开发复杂度。
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
2.1 准备数据库连接
在JDBC操作数据库时为了方便,我们直接将数据库的连接信息写到了JAVA类里,在使用MyBatis以及后续的开发当中,为了避免数据库连接信息与代码耦合,规范的方式是将数据库连接信息与代码分开,我们这里将数据库连接信息写到db.properties配置文件里。
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/auth?characterEncoding=utf-8&useSSL=false
jdbc.username=root
jdbc.password=root
2.2 准备Mybatis配置文件
我们在MyBatis官网入门章节可以找到mybatis的配置文件结构,配置文件的名字无所谓:
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" />
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<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>
2.2.1 properties(属性配置)
在mybatis的配置文件中,这里我们配置了几个,一个是,这个节点是告诉mybatis, 去这个路径加载这个配置文件,在后续配置datasource以及mappers节点的时候,可以使用配置文件里的内容。
2.2.2 environments(环境信息)
在实际开发中,我们开发环境,测试环境以及生产环境数据库一般是分开的,连接信息也就不同,environments节点可以配置不同环境的连接信息,这里我们只配置一个environment,因为我们只有一个环境。
2.2.2.1 transactionManager(事务管理器)
事务管理器节点,这个节点的配置一般是固定的配置为JDBC,mybatis提供了两种类型的事务管理器,JDBC/MANAGED。
JDBC配置直接使用JDBC的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
2.2.2.2 dataSource(数据源)
mybatis有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"),一般情况下我们使用池化的数据源,避免频繁与数据库创建连接,关闭连接,这样可以提高与数据库的交互效率。除了四个必需的参数driver,url,username, password,我们还可以配置连接的超时时间,默认的连接事务隔离级别。具体参数可以查看官网。
2.3 准备Mapper映射文件(映射器)
MyBatis被广泛使用就是因为SQL语句与java语句的优雅地映射。同样,我们在mybatis官网获取mapper映射文件的模板。这里如果大家用的是IDEA,推荐大家下载Free ByBatis Plugin这个插件,很方便从mapper接口到mybatils的xml映射文件间相互跳转。
StudentMapper.java:
package org.jeanerk.mappers;
import org.jeanerk.mybatis.entity.Student;
public interface StudentMapper {
Student selectStudentById(Long id);
}
StudentMapper.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="org.jeanerk.mappers.StudentMapper">
<select id="selectStudentById" resultType="org.jeanerk.mybatis.entity.Stutent">
select * from student where sid = #{id}
</select>
</mapper>
2.4 构建SqlSessionFactory
到这里我们基本把需要的mybatis的配置信息,数据库的连接信息都准备好了,下面开始进入实际使用mybatis环节。
// 读取MyBatis配置文件
InputStream mybatisConfigs = Resources.getResourceAsStream("mybatisconfig.xml");
// 通过SqlSessionFactoryBuilder构建SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(mybatisConfigs);
2.5 获取SqlSession
// openSession还有一个重构可以接收一个参数boolean autoCommit, 是否自动提交,默认不提交。
SqlSession sqlSession = factory.openSession();
2.6 获取Mapper
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
2.7 执行
需要注意的是,mybatis将数据库查询结果与java bean映射需要java bean的set方法的去除set后的内容的首字母小写与数据库对应字段相同。否则会导致字段映射不上,java bean里面没有对应的值。还有一种方式是通过自己配置resultMap.
例如:数据库字段是sname,类型是VARCHAR, java bean里面对应的字段的set方法必须是setSname(String name).
Student student = mapper.selectStudentById(1L);