MyBatis HelloWorld

2017-09-27  本文已影响0人  白菜饼

☃ 数据库准备

create table tb_student  
(  
    Sno varchar(20),  
    Sname varchar(50),    
    primary key (Sno)  
) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
  
create table tb_course  
(  
    Cno varchar(20),  
    Cname varchar(50),    
    Tno varchar(20),  
    primary key (Cno)  
) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
  
create table tb_sc  
(  
    Sno varchar(20),  
    Cno varchar(20),      
    tb_score int,  
    primary key (Sno,Cno)  
) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
  
create table tb_teacher  
(  
    Tno varchar(20),  
    Tname varchar(50),    
    primary key (Tno)  
) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
  
INSERT INTO `tb_student`(Sno,Sname) VALUES ('001','陈一');  
INSERT INTO `tb_student`(Sno,Sname) VALUES ('002','郭二');  
INSERT INTO `tb_student`(Sno,Sname) VALUES ('003','张三');  
INSERT INTO `tb_student`(Sno,Sname) VALUES ('004','李四');  
INSERT INTO `tb_student`(Sno,Sname) VALUES ('005','王五');  
  
INSERT INTO `tb_teacher`(Tno,Tname) VALUES ('001','张老师');  
INSERT INTO `tb_teacher`(Tno,Tname) VALUES ('002','王老师');  
INSERT INTO `tb_teacher`(Tno,Tname) VALUES ('003','钱老师');  
INSERT INTO `tb_teacher`(Tno,Tname) VALUES ('004','刘老师');  
INSERT INTO `tb_teacher`(Tno,Tname) VALUES ('005','胡老师');  
  
INSERT INTO `tb_course`(Cno,Cname,Tno) VALUES ('001','语文','张老师');  
INSERT INTO `tb_course`(Cno,Cname,Tno) VALUES ('002','数学','王老师');  
INSERT INTO `tb_course`(Cno,Cname,Tno) VALUES ('003','英语','钱老师');  
INSERT INTO `tb_course`(Cno,Cname,Tno) VALUES ('004','物理','刘老师');  
INSERT INTO `tb_course`(Cno,Cname,Tno) VALUES ('005','政治','胡老师');  
  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('001','001',50);  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('001','002',60);  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('001','003',70);  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('001','004',80);  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('001','005',90);  
  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('002','001',90);  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('002','002',80);  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('002','003',70);  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('002','004',60);  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('002','005',50);  
  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('003','001',81);  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('003','002',82);  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('003','003',83);  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('003','004',84);  
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('003','005',85); 

JDBC方式

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.38</version>
</dependency>
public static void main(String[] args) throws Exception {
    Connection connection = null;
    PreparedStatement prepareStatement = null;
    ResultSet rs = null;
    try {
        // 1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2.获取连接
        String url = "jdbc:mysql://localhost:3306/test_mybatis?useSSL=false";
        String user = "root";
        String password = "root";
        connection = DriverManager.getConnection(url, user, password);
        // 3.获取statement||preparedStatement
        String sql = "select * from tb_student where sno=?";
        prepareStatement = connection.prepareStatement(sql);
        // 4.设置参数
        prepareStatement.setLong(1, "001");     //从1开始不是0
        // 5.执行查询
        rs = prepareStatement.executeQuery();
        // 6.处理结果集
        while (rs.next()) {
            System.out.println(rs.getString("sno"));
            System.out.println(rs.getString("sname"));
        }
    } finally {
        // 7.关闭连接,释放资源
        if (rs != null) {
            rs.close();
        }
        if (prepareStatement != null) {
            prepareStatement.close();
        }
        if (connection != null) {
            connection.close();
        }
    }
}
    1.四大金刚硬编码;SQL与代码耦合
    2.每次都要加载驱动,获得/关闭connection/statement/resultSet
    3.传参需要判断参数类型和下标,结果集获得结果需要判断类型和下标/列名

❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤

MyBatis

🍀 MyBaits整体架构
image.png
A.根据创建SqlSessionFactory的方式,分成xml和annotation两种方式:
   1.xml方式需要mybatis-cofig.xml和xxMapper.xml(映射文件也可以用注解代替,但复杂SQL仍需在xxMapper.xml中书写)
   2.注解方式要全注解,读不到mybatis-cofig.xml和xxxMapper.xml???(待确认)
B.创建SqlSessionFactory和SqlSession后,根据[接口方法-Statement]的映射方式,又可分成一般和动态代理两种方式:
   1.一般方式容易出现字符串(namespace.statementId)拼写错误
   2.动态代理方式推荐使用,但须遵循4个约定
🍀 HelloWorld(3版)
①HelloWorld(最简xml版)
<dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <!-- mybatis支持了slf4j日志系统 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>
log4j.rootLogger=DEBUG,A1
log4j.logger.org.apache=DEBUG
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test_mybatis?useSSL=false
username=root
password=root
<?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>
    <!-- 读取jdbc四大金刚 -->
    <properties resource="jdbc.properties"></properties>
    
    <!-- 环境配置 -->
    <environments default="development">
      <environment id="development">
        <!-- 事务管理 -->
        <transactionManager type="JDBC"/>
        <!-- 数据源 -->
        <dataSource type="POOLED">
          <property name="driver" value="${driver}"/>
          <property name="url" value="${url}"/>
          <property name="username" value="${username}"/>
          <property name="password" value="${password}"/>
        </dataSource>
      </environment>
    </environments>
    
    <!-- 映射文件 -->
    <mappers>
      <mapper resource="StudentMapper.xml"/>
    </mappers> 
</configuration>
public class Student {
    private String sno;
    private String sname;
    public String getSno() {
        return sno;
    }
    public void setSno(String sno) {
        this.sno = sno;
    }
    public String getSname() {
        return sname;
    }
    public void setSname(String sname) {
        this.sname = sname;
    }
    @Override
    public String toString() {
        return "Student [sno=" + sno + ", sname=" + sname + "]";
    }
}
<?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="123">
    <select id="selectById" resultType="com.top.test.pojo.Student">
        select * from tb_student where sno = #{sno}
    </select>
</mapper>
public class MybatisTest {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        
        try {
            Student student = sqlSession.selectOne("123.selectById", "001");
            System.out.println(student);
        } finally {
            sqlSession.close();
        }
    }
}

如果这里用SqlSession sqlSession = sqlSessionFactory.openSession(true);(重载方法,会自动提交),那么做增删改时不用手动提交了:this.sqlSession.commit();

注:简单版的helloworld没有写Mapper接口和实现类

2017-09-26 12:10:34,623 [main] [org.apache.ibatis.logging.LogFactory]-[DEBUG] Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
2017-09-26 12:10:34,707 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2017-09-26 12:10:34,707 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2017-09-26 12:10:34,707 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2017-09-26 12:10:34,707 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2017-09-26 12:10:34,763 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
2017-09-26 12:10:34,954 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 239465106.
2017-09-26 12:10:34,954 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@e45f292]
2017-09-26 12:10:34,956 [main] [123.selectById]-[DEBUG] ==>  Preparing: select * from tb_student where sno = ? 
2017-09-26 12:10:34,987 [main] [123.selectById]-[DEBUG] ==> Parameters: 001(String)
2017-09-26 12:10:35,001 [main] [123.selectById]-[DEBUG] <==      Total: 1
Student [sno=001, sname=陈一]
2017-09-26 12:10:35,001 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@e45f292]
2017-09-26 12:10:35,002 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@e45f292]
2017-09-26 12:10:35,002 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Returned connection 239465106 to pool.
image.png

☃ 分析

- A.步骤
  - 1.配置mybatis-config.xml 全局的配置文件 (1.数据源,2.xxxMapper.xml) 
  - 2.创建SqlSessionFactory,打开SqlSession
  - 4.通过SqlSession操作数据库 CRUD
  - 5.调用sqlSession.commit()提交事务(增删改)
  - 6.调用sqlSession.close()关闭会话
- B.注意事项
  - 1.这里直接使用SqlSession进行CRUD,可以使用Mapper接口和其实现类,也可以使用动态代理方式(底层都是sqlSession)
  - 2.因为没有写Mapper接口,映射文件的namespace名和statementId是随意取得,只要保证namespace唯一&&该namespace下该id唯一即可。一般与Mapper接口一致
②HelloWorld(完整xml版)

完整版在最简版的基础上:a.删除了测试类;b.增加Mapper接口和实现类;c.使用单元测试用例

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>
public interface StudentMapper {
    //@Select("select * from tb_student where sno = #{sno}")
    public Student selectById(String sno);
    
    public List<Student> selectAll();
    
    public int selectCount();
    
    //CUD默认返回int(影响条数,batchUpdate似乎不行),直接Integer接收不需要设置resultType;若要返回主键需要Statement标签中设置
    public Integer insertStu(Student student);
    
    public Integer updateStu(Student student);
    
    public void deleteById(String sno);
}
public class StudentMapperImpl implements StudentMapper {

    public SqlSession sqlSession;
    
    //有参构造
    public StudentMapperImpl(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }

    public Student selectById(String sno) {
        return this.sqlSession.selectOne("123.selectById", sno);
    }

    public List<Student> selectAll() {
        return this.sqlSession.selectList("123.selectAll");
    }
    
    public int selectCount() {
        return this.sqlSession.selectOne("123.selectCount");
    }
    
    //CUD默认返回int(影响条数),要返回主键需要Statement标签中设置
    public Integer insertStu(Student student) {
        return this.sqlSession.insert("123.insert", student);
        
    }

    public Integer updateStu(Student student) {
        return this.sqlSession.update("123.update", student);
    }

    public void deleteById(String sno) {
        this.sqlSession.delete("123.delete", sno);
    }
}

☞ 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="123">

    <select id="selectById" resultType="com.top.test.pojo.Student">
        select * from tb_student where sno = #{sno};
    </select>
    
    <select id="selectAll" resultType="com.top.test.pojo.Student">
        select * from tb_student;
    </select>
    
    <!-- 返回int/integer都可以,integer也可以用int接,但一般对应起来都用integer -->
    <select id="selectCount" resultType="Integer">
        select count(*) from tb_student;
    </select>
    
    <insert id="insert" parameterType="com.top.test.pojo.Student">
        insert into tb_student(
            sno,
            sname
        )
        values(
            #{sno},
            #{sname}
        );
    </insert>
    
    <update id="update" parameterType="com.top.test.pojo.Student">
        upadte tb_student set
            sname = #{sname}
        where
            sno = #{sno};
    </update>
    
    <delete id="delete" parameterType="String">
        delete from tb_student where sno = #{sno};
    </delete>
    
</mapper>

这里的namespace和id是随意取得,一般和Mapper接口方法一致;使用动态代理时必须要一致

public class StudentMapperTest {
    public SqlSession sqlSession;
    public StudentMapper studentMapper;
    
    @Before
    //@Test方法执行前执行该方法
    public void setUp() throws Exception {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //不能自己加SqlSession sqlSession(加了就是局部变量,setUp执行后就没了),所以要使用成员变量,不能自己new一个引用
        this.sqlSession = sqlSessionFactory.openSession();
        //同上;通过有参构造传入sqlSession
        this.studentMapper = new StudentMapperImpl(sqlSession);
    }

    @Test
    public void testSelectById() {
        Student student = this.studentMapper.selectById("001");
        System.out.println(student);
    }

    @Test
    public void testSelectAll() {
        List<Student> list = this.studentMapper.selectAll();
        for (Student student : list) {
            System.out.println(student);
        }
    }
    
    @Test
    public void testSelectCount() {
        int count = this.studentMapper.selectCount();
        System.out.println(count);
    }

    @Test
    public void testInsertStu() {
        Student student = new Student();
        student.setSno("006");
        student.setSname("周六");
        Integer count = this.studentMapper.insertStu(student);
        this.sqlSession.commit();//不提交不生效;使用重载方法可以自动提交事务
        System.out.println(count);
    }

    @Test
    public void testUpdateStu() {
        Student student = new Student();
        student.setSno("006");
        student.setSname("赵七");
        Integer count = this.studentMapper.updateStu(student);
        this.sqlSession.commit();//不提交不生效;使用重载方法可以自动提交事务
        System.out.println(count);
    }

    @Test
    public void testDeleteById() {
        this.studentMapper.deleteById("006");
        this.sqlSession.commit();//不提交不生效;使用重载方法可以自动提交事务
    }
}

sqlSession本身的两个实现类是线程不安全的(mybatis-spring提供的SqlSessionTemplate是线程安全的实现),所以这里生命周期最好是一个线程(如一次request-response)/方法内-局部变量

image.png

☃ 分析执行顺序

- 1.Run as,会先执行@Before方法
  - 创建sqlSession(加载主配置文件mybatis-config.xml),赋值给成员变量引用
  - 实例StudentMapperImpl(传sqlSession)
- 2.再执行StudentMapperTest中的@Test方法
  - 测试StudentMapperImpl的方法-->也就是sqlSession执行相应的statement
- 3.提交,数据库更新
③HelloWorld(动态代理版)

因为在DAO/Mapper的实现类中对sqlsession的使用方式很类似。mybatis提供了接口的动态代理(懒)

     ➢ namespace名称 = 接口Mapper/DAO的全类名
     ➢ statement的id = 接口Mapper/DAO的方法名
     ➢ resultType = mapper接口方法的返回类型
     ➢ parameterType = mapper接口方法的参数类型(也可省略不写)
使用动态代理改造CRUD

a.删除DAO/Mapper接口实现类;b.满足四个约定;c.修改单元测试(接口方法-Statement的映射方式)

<?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.top.test.mapper.StudentMapper">
    <select id="selectById" resultType="com.top.test.pojo.Student">
        select * from tb_student where sno = #{sno};
    </select>
    
    <select id="selectAll" resultType="com.top.test.pojo.Student">
        select * from tb_student;
    </select>
    
    <!-- 返回int/integer都可以,integer也可以用int接,但一般对应起来都用integer -->
    <select id="selectCount" resultType="Integer">
        select count(*) from tb_student;
    </select>
    
    <insert id="insertStu" parameterType="com.top.test.pojo.Student">
        insert into tb_student(
            sno,
            sname
        )
        values(
            #{sno},
            #{sname}
        );
    </insert>
    
    <update id="updateStu" parameterType="com.top.test.pojo.Student">
        upadte tb_student set
            sname = #{sname}
        where
            sno = #{sno};
    </update>
    
    <delete id="deleteById" parameterType="String">
        delete from tb_student where sno = #{sno};
    </delete>   
</mapper>
public class StudentMapperTest {
    public SqlSession sqlSession;
    public StudentMapper studentMapper;
    
    @Before
    //@Test方法执行前执行该方法
    public void setUp() throws Exception {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //不能自己加SqlSession sqlSession(加了就是局部变量,setUp执行后就没了),所以要使用成员变量,不能自己new一个赋给引用
        this.sqlSession = sqlSessionFactory.openSession();
        //this.studentMapper = new StudentMapperImpl(sqlSession);//同上;通过有参构造传入sqlSession
        //同上;动态代理方式
        this.studentMapper = this.sqlSession.getMapper(StudentMapper.class);
    }

    @Test
    public void testSelectById() {
        Student student = this.studentMapper.selectById("001");
        System.out.println(student);
    }

    @Test
    public void testSelectAll() {
        List<Student> list = this.studentMapper.selectAll();
        for (Student student : list) {
            System.out.println(student);
        }
    }
    
    @Test
    public void testSelectCount() {
        int count = this.studentMapper.selectCount();
        System.out.println(count);
    }

    @Test
    public void testInsertStu() {
        Student student = new Student();
        student.setSno("006");
        student.setSname("周六");
        Integer count = this.studentMapper.insertStu(student);
        this.sqlSession.commit();//不提交不生效;使用重载方法可以自动提交事务
        System.out.println(count);
    }

    @Test
    public void testUpdateStu() {
        Student student = new Student();
        student.setSno("006");
        student.setSname("赵七");
        Integer count = this.studentMapper.updateStu(student);
        this.sqlSession.commit();//不提交不生效;使用重载方法可以自动提交事务
        System.out.println(count);
    }

    @Test
    public void testDeleteById() {
        this.studentMapper.deleteById("006");
        this.sqlSession.commit();//不提交不生效;使用重载方法可以自动提交事务
    }
}
☠ MyBatis HelloWorld Done☠

上一篇 下一篇

猜你喜欢

热点阅读