从JDBC到MyBatis

从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', '女');

1. 通过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;
    }

2. 通过MyBatis操作数据库

MyBatis最终也是通过JDBC对数据库进行操作,只不过把加载数据库连接驱动,创建数据库连接,准备PrepareStatement,将查询结果集封装为java实体的操作帮我们实现了。极大简化了我们的开发复杂度。

同样地,我们要使用MyBatis,必须先引入MyBatis的jar包, 同样,数据库驱动jar包也不可少(见1.1)。

<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的配置文件中,这里我们配置了几个,一个是<propertis resources="jdbc.properties"/>,这个节点是告诉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);

完整示例:

package org.jeanerk.mybatis;
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.jeanerk.mappers.StudentMapper;
import org.jeanerk.mybatis.entity.Student;

import java.io.IOException;
import java.io.InputStream;
public class MyBatisApp {
    public static void main(String[] args) throws IOException {
        InputStream mybatisConfigs = Resources.getResourceAsStream("mybatisconfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(mybatisConfigs);
        try(SqlSession sqlSession = factory.openSession()) {
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
            Student student = mapper.selectStudentById(1L);
            System.out.println(student);
        }
    }
}

通过定义resultMap的方式处理:

    <resultMap id="studentMap" type="org.jeanerk.mybatis.entity.Student">
        <result column="SID" property="id" javaType="java.lang.Long"/>
        <result column="SNAME" property="name" javaType="String" />
        <result column="SAGE" property="birthday" javaType="String" />
        <result column="SSEX" property="sex" javaType="String" />
    </resultMap>

    <select id="selectStudentByName" resultMap="studentMap">
        select SNAME, SAGE, SSEX, SID FROM STUDENT WHERE SNAME LIKE CONCAT('%', #{name}, '%')
    </select>

注意这里模糊查询的写法,mysql和oracle数据库用这种写法。sqlserver的写法如下:

    <select id="selectStudentByName" resultMap="studentMap">
        select SNAME, SAGE, SSEX, SID FROM STUDENT WHERE SNAME LIKE '%'+ #{name} + '%'
    </select>

对应的接口:

    List<Student> selectStudentByName(String name);

完整示例:

package org.jeanerk.mappers;

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.jeanerk.mybatis.entity.Student;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class StudentMapperTest {

    SqlSessionFactory sessionFactory;

    @Before
    public void before() throws IOException {
        InputStream mybatisConfig = Resources.getResourceAsStream("mybatisconfig.xml");
        sessionFactory = new SqlSessionFactoryBuilder().build(mybatisConfig);
    }

    @Test
    public void testSelectByResultMap() {
        SqlSession sqlSession = sessionFactory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> studentList = mapper.selectStudentByName("三");
        System.out.println(studentList);
    }

    @Test
    public void testSelectByResultType() throws IOException {
        SqlSession sqlSession = sessionFactory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = mapper.selectStudentById(1L);
        System.out.println(student);
    }

    @Test
    public void testInsertStudent() throws IOException {
        SqlSession sqlSession = sessionFactory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = new Student();
        student.setSId(22L);
        student.setSName("张无忌");
        student.setSAge("2020-10-28 16:14:22");
        student.setSSex("男");
        boolean saveResult = mapper.saveStudent(student);
        // sessionFactory.openSession()拿到的sqlSession,autoCommit默认为false, 并不会自动提交事务,因些需要手动提交
        sqlSession.commit();
        System.out.println(saveResult);
    }
}

这里使用了JUNIT进行测试,在Junit中,通过@Before注解的方法会在测试方法前先执行,我们可以把SqlSessionFactory的初始化工作放到@Before注解的方法中。

StudentMapper.java

package org.jeanerk.mappers;

import org.jeanerk.mybatis.entity.Student;

import java.util.*;

public interface StudentMapper {
    Student selectStudentById(Long id);

    List<Student> selectStudentByName(String name);

    boolean saveStudent(Student s);
}

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">
    <resultMap id="studentMap" type="org.jeanerk.mybatis.entity.Student">
        <result column="SID" property="id" javaType="java.lang.Long"/>
        <result column="SNAME" property="name" javaType="String" />
        <result column="SAGE" property="birthday" javaType="String" />
        <result column="SSEX" property="sex" javaType="String" />
    </resultMap>

    <select id="selectStudentByName" resultMap="studentMap">
        select SNAME, SAGE, SSEX, SID FROM STUDENT WHERE SNAME LIKE CONCAT('%', #{name}, '%')
    </select>

    <select id="selectStudentById" resultType="org.jeanerk.mybatis.entity.Student">
        select * from student where sid = #{id}
    </select>

    <insert id="saveStudent" parameterType="org.jeanerk.mybatis.entity.Student">
        INSERT INTO STUDENT(SID, SNAME, SAGE, SSEX) VALUES(${id}, #{name}, #{birthday}, #{sex})
    </insert>
</mapper>

Student.java:

package org.jeanerk.mybatis.entity;

public class Student {
    Long id;
    String name;
    String birthday;
    String sex;
    public Long getSId() {
        return id;
    }
    public void setSId(Long id) {
        this.id = id;
    }
    public String getSName() {
        return name;
    }
    public void setSName(String name) {
        this.name = name;
    }
    public String getSAge() {
        return birthday;
    }
    public void setSAge(String birthday) {
        this.birthday = birthday;
    }
    public String getSSex() {
        return sex;
    }
    public void setSSex(String sex) {
        this.sex = sex;
    }
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", birthday='" + birthday + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}

注意Student.java中属性的set方法,回忆select标签resultType方式映射时,数据库字段与java实体是怎么映射的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值