• Mybatis基本使用


    一:Mybatis通过实现类完成操作(了解)

      在上一篇博客说到,mybatis通过实现类的操作很少用到,但是我还得抽空把这一章详细地说一下,因为我们现在是学习,所以还是都了解一下吧,在后期的话,我会单独写出一篇博文来分析一下mybatis源码,好了,废话也不多说了,我打算通过使用实现类完成CRUD操作,因为这一章不是重要学习对象,所以我的注释不是太详细

    1 :准备工作

    建表建库语句     log4j.properties配置

    ①:整体框架

     ②:框间主体代码编写

    <dependencies>
            <!--mysql驱动包-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.32</version>
            </dependency>
            <!--mybatis坐标-->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.4.6</version>
            </dependency>
            <!--log日志坐标-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
            <!--单元测试坐标-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
        </dependencies>
    坐标文件 pom.xml
     1 <?xml version="1.0" encoding="UTF-8" ?>
     2 <!DOCTYPE configuration
     3         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
     4         "http://mybatis.org/dtd/mybatis-3-config.dtd">
     5 <configuration>
     6     <environments default="mysql">
     7         <environment id="mysql">
     8             <transactionManager type="JDBC"></transactionManager>
     9             <dataSource type="POOLED">
    10                 <!--四大配置 driver驱动 url数据库地址 username/password账号密码-->
    11                 <property name="driver" value="com.mysql.jdbc.Driver"/>
    12                 <property name="url" value="jdbc:mysql://localhost:3306/demo_school"/>
    13                 <property name="username" value="root"/>
    14                 <property name="password" value="123"/>
    15             </dataSource>
    16         </environment>
    17     </environments>
    18     <!--映射  找到资源路径下的StudentDao.xml-->
    19     <!--这里说一下  类路径下(src路径下要使用点) 而资源路径下要使用斜杠 -->
    20     <mappers>
    21         <mapper resource="cn/xw/dao/StudentDao.xml"></mapper>
    22     </mappers>
    23 </configuration>
    主配置文件编写 SqlMapConfig.xml
    /**
     * @author ant
     * bean 学生实体类
     */
    public class Student {
        private int sid;            //id
        private String sname;       //姓名
        private String ssex;        //性别
        private int sage;           //年龄
        private double sscore;      //成绩/学分
        private double smoney;      //零花钱
        private String saddress;    //住址
        private String senrol;      //入学时间
        private int tid;            //外键 老师id
        
        //下面你们自己加构造器/get/set/toString
    }
    实体类 Student
    注:这个是抽象接口 StudentDao
    
    /**
     * @ant
     * @interface 接口操作学生数据
     */
    public interface StudentDao {
        //查询全部学生
        List<Student> findAll();
    }
    
    +++++++++++++++++++++++++++++++++++++++++
    
    注:这个实现类接口 StudentDaoImpl
    
    /**
     * @author ant
     * @class 具体的StudentDao的实现类
     */
    public class StudentDaoImpl implements StudentDao {
    
        //因为是实现类操作,所以我不能使用代理,那就把工厂聚合到实现类,让它们自己调用
        private SqlSessionFactory factory;
    
        //构造函数,对工厂赋值
        public StudentDaoImpl(SqlSessionFactory factory) {
            this.factory = factory;
        }
    
        public List<Student> findAll() {
            //通过工厂对象获取一个实例对象
            SqlSession sqlSession = factory.openSession();
            //通过调用方法来完成对数据的查询
            List<Student> list = sqlSession.selectList("cn.xw.dao.StudentDao.findAll");
            //关闭实例对象
            sqlSession.close();
            //返回数据
            return list;
        }
    
    }
    抽象接口及实现类
    <?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="cn.xw.dao.StudentDao">
        <!--查询全部学生-->
        <select id="findAll" resultType="cn.xw.domain.Student">
            select * from student;
        </select>
    </mapper>
    StudentDao对应的xml
    public class Client {
        //配置文件流
        private InputStream config;
        //配置builder可以直接创建工厂类
        private SqlSessionFactoryBuilder builder;
        //工厂类
        private SqlSessionFactory factory;
        
        //这个注解是单元测试  代表测试方法执行前执行 参数设置
        @Before
        public void init() throws IOException {
            //这里特别要注意  我前面没说,Resources是import org.apache.ibatis.io.Resources;包下的
            config=Resources.getResourceAsStream("SqlMapConfig.xml");
            builder=new SqlSessionFactoryBuilder();
            factory=builder.build(config);
        }
    
        //这个注解是单元测试  代表测试方法执行后执行 关闭资源
        @After
        public void destroy() throws IOException {
            //关闭流对象
            config.close();
        }
    
        //查询全部
        @Test
        public void findAll(){
            //调用实体类传入之前聚合的工厂
            StudentDao dao=new StudentDaoImpl(factory);
            //获取资源
            List<Student> students = dao.findAll();
            //打印
            for(Student student:students){
                System.out.println(student);
            }
        }
    
    }
    测试编码

    ③:说明

    写到这里后框架的整体部分已经编写完成了,我在这个框架里面有一个测试方法,查询全部数据,如果可以运行成功则框架搭建完成,接下来只需要在里面添加相对于的方法就可以完成对代码的增删改查了

    2:实现CRUD操作

    在编写CRUD的代码之前,首先说明一下,下面的编写的都是代码片段,只需要在原基础的框架上编写即可

    注:这下面一段介绍必看,方法介绍

    mybatis其实也为我们提供了自己编写实现类,而且还有大量的方法

     1 注:其实方法原不止这些,我挑选一些经常使用的代码在这里做介绍
     2 后期随着我对知识的不断深入会不断的对博文进行调整
     3 
     4 参数介绍:
     5 String statement:其实这个参数是一个声明的意思,就是要我们开发者告知当前的配置在哪及配置的里面哪一项
     6 有点抽象,我举个例子
     7 假设现在有个selectOne被调用后传入一下示例的参数
     8 selectOne("cn.xw.dao.StudentDao.findById")
     9 这就代表cn.xw.dao.StudentDao找到了指定的抽象接口,但是找到这个接口有毛用呀,错,别忘了这个接口不是有个对应的xml配置文件吗?没错,就是找到那边了,因为就是通过namespace的配置让它们联系起来,然后不是还有一个findById后缀吗,这个都不陌生了,就是找当前下面的xml配置的id,
    10 
    11 Object parameter:这个就好理解了,让我们传入一个Object类型的参数,如果有多个参数只能使用封装的对象或者Map和List传入过去,后面会详细说明
    12 
    13 
    14 
    15 查询:
    16 
    17 注:其实大家也看出来,selectOne代表查询一行多列的数据,selectList代表查询多行多列的数据啦
    18 
    19 selectOne(String statement)
    20 
    21 selectOne(String statement, Object parameter)
    22 
    23 selectList(String statement)
    24 
    25 selectList(String statement, Object parameter) 
    26 
    27 更新:
    28 update(String statement)
    29 
    30 update(String statement, Object parameter) 
    31 添加:
    32 
    33 insert(String statement) 
    34 
    35 insert(String statement, Object parameter)
    36 删除:
    37 
    38 delete(String statement) 
    39 
    40 delete(String statement, Object parameter)
    方法介绍

    ①:查询单个数据 findById

    注:StudentDao抽象类
    
    //根据学生id查询
        Student findById(Integer id);
    
    
    ++++++++++++++++++++++++++++++++++++++++++
    
    注:抽象类实现StudentDaoImpl
    
    //根据ID查询数据
        public Student findById(Integer id) {
            SqlSession sqlSession = factory.openSession();
            Student student = sqlSession.selectOne("cn.xw.dao.StudentDao.findById", id);
            sqlSession.close();
            return student;
        }
    抽象类/抽象类实现
    <!--根据学生id查询-->
        <select id="findById" parameterType="Integer" resultType="cn.xw.domain.Student">
            select * from student where sid=#{id}
        </select>
    StudentDao.xml
    //根据学生id查询
        @Test
        public void findById(){
            StudentDao dao=new StudentDaoImpl(factory);
            Student student = dao.findById(20);
            System.out.println(student.toString());
        }
    测试类

     ②:根据学生的性别和年龄查询 findBySexAndAge

    注:这边的参数传入,因为有多个,而接收的只能是一个,所以我封装到map里面,后期详细接收

    注:StudentDao抽象类
    
    //根据学生性别和年龄来查询学生
        List<Student> findBySexAndAge(String sex, Integer age);
    
    
    +++++++++++++++++++++++++++++++++++++++++
    
    
    注:抽象类实现StudentDaoImpl
    
    //根据性别 年龄查询数据
       public List<Student> findBySexAndAge(String sex, Integer age) {
            SqlSession sqlSession = factory.openSession();
            Map<String,Object> maps=new HashMap<String, Object>();
            maps.put("sex",sex);
            maps.put("age",age);
            List<Student> list = sqlSession.selectList("cn.xw.dao.StudentDao.findBySexAndAge", maps);
            sqlSession.close();
            return list;
        }
    抽象类/抽象类实现
    <!--根据学生性别和年龄来查询学生-->
        <select id="findBySexAndAge" resultType="cn.xw.domain.Student">
            select * from student where ssex=#{sex} and sage=#{age}
        </select>
    StudentDao.xml
    //根据学生性别和年龄来查询学生
        @Test
        public void findBySexAndAge(){
            StudentDao dao=new StudentDaoImpl(factory);
            List<Student> students = dao.findBySexAndAge("男", 22);
            for(Student student:students){
                System.out.println(student);
            }
        }
    测试类

     ③:根据姓名模糊查询 findByLikeName

    注:这个like传值下面后详细介绍

    注:StudentDao抽象类
    
      //根据姓名模糊查询
        List<Student> findByLikeName(String name);
    
    
    +++++++++++++++++++++++++++++++++++++++++
    
    
    注:抽象类实现StudentDaoImpl
    
    //根据姓名模糊查询
    public List<Student> findByLikeName(String name) {
            SqlSession sqlSession = factory.openSession();
            List<Student> list = sqlSession.selectList("cn.xw.dao.StudentDao.findByLikeName", "%" + name + "%");
            sqlSession.close();
            return list;
        }
    抽象类/抽象类实现
    <!--根据姓名模糊查询-->
        <select id="findByLikeName" parameterType="String" resultType="cn.xw.domain.Student">
            select * from student where sname like #{name};
        </select>
    StudentDao.xml
    //根据姓名模糊查询
        @Test
        public void findByLikeName(){
            StudentDao dao=new StudentDaoImpl(factory);
            List<Student> students = dao.findByLikeName("张");
            for(Student student:students){
                System.out.println(student);
            }
        }
    测试类

     ④:添加数据 save

    注:StudentDao抽象类
    
      //添加学生
        void save(Student student);
    
    
    +++++++++++++++++++++++++++++++++++++++++
    
    
    注:抽象类实现StudentDaoImpl
    
    //添加数据   注意要提交事务commit
    public void save(Student student) {
            SqlSession sqlSession = factory.openSession();
            sqlSession.insert("cn.xw.dao.StudentDao.save", student);
            sqlSession.commit();
            sqlSession.close();
        }
    抽象类/抽象类实现
     <!--添加学生-->
        <insert id="save" parameterType="cn.xw.domain.Student">
            insert into student (sname,ssex,sage,sscore,smoney,saddress,senrol,tid) values
            (#{sname},#{ssex},#{sage},#{sscore},#{smoney},#{saddress},#{senrol},#{tid});
        </insert>
    StudentDao.xml
    //添加学生
        @Test
        public void save(){
            StudentDao dao=new StudentDaoImpl(factory);
            Student student=new Student(0,"老王","男",22,25.5,525.0,"安徽六安","2017-8-8",4);
            dao.save(student);
        }
    测试类

    ⑤:更改数据 update

    注:StudentDao抽象类
    
      //更改学生 姓名 住址
        void update(Student student);
    
    
    +++++++++++++++++++++++++++++++++++++++++
    
    
    注:抽象类实现StudentDaoImpl
    
    //根据传入的数据对象更改
    public void update(Student student) {
            SqlSession sqlSession = factory.openSession();
            sqlSession.update("cn.xw.dao.StudentDao.update", student);
            sqlSession.commit();
            sqlSession.close();
        }
    抽象类/抽象类实现
    <!--更改学生 姓名  住址-->
        <update id="update" parameterType="cn.xw.domain.Student">
            update student set sname=#{sname} , saddress=#{saddress} where sid=#{sid}
        </update>
    StudentDao.xml
    //更改学生 姓名 住址
        @Test
        public void update(){
            StudentDao dao=new StudentDaoImpl(factory);
            Student student=new Student(25,"蚂蚁小哥","",0,0,0,"北京顺义","",0);
            dao.update(student);
        }
    测试类

    ⑥:删除学生 delete

    注:StudentDao抽象类
    
      //删除学生
        void delete(Integer id);
    
    
    +++++++++++++++++++++++++++++++++++++++++
    
    
    注:抽象类实现StudentDaoImpl
    
    //删除指定id学生  注意事务提交
    public void delete(Integer id) {
            SqlSession sqlSession = factory.openSession();
            sqlSession.delete("cn.xw.dao.StudentDao.delete", id);
            sqlSession.commit();
            sqlSession.close();
        }
    抽象类/抽象类实现
    <!--删除学生-->
        <delete id="delete" parameterType="Integer">
            delete from student where sid=#{id}
        </delete>
    StudentDao.xml
    //删除学生
        @Test
        public void delete(){
            StudentDao dao=new StudentDaoImpl(factory);
            dao.delete(66);
        }
    测试类

    ⑦:聚合查询  totalCount

    注:StudentDao抽象类
    
     //查询学生总数(聚合查询)
        int totalCount();
    
    
    +++++++++++++++++++++++++++++++++++++++++
    
    
    注:抽象类实现StudentDaoImpl
    
    //查询总记录数
    public int totalCount() {
            SqlSession sqlSession = factory.openSession();
            int total = sqlSession.selectOne("cn.xw.dao.StudentDao.totalCount");
            return total;
        }
    抽象类/抽象类实现
    <!--查询学生总数(聚合查询)-->
        <select id="totalCount" resultType="Integer">
            select count(sid) from student;
        </select>
    StudentDao.xml
    //查询学生总数(聚合查询)
        @Test
        public void totalCount(){
            StudentDao dao=new StudentDaoImpl(factory);
            int total = dao.totalCount();
            System.out.println("总数:"+total);
        }
    测试类

    二:Mybatis之代理模式操作数据(重点

    注:我会在这里面详细解释一些配置及作用,而且也是开发中经常用到的方法之一

    1:框架搭建

    有了之前的实现类操作的框架基础,大家只需要删除实现类Impl及下面的全部文件删除即可,在主测试类中也只需要删除那些测试方法即可

    public class Client {
        //配置文件流
        private InputStream config;
        //配置builder可以直接创建工厂类
        private SqlSessionFactoryBuilder builder;
        //工厂类
        private SqlSessionFactory factory;
        //SqlSession实例对象
        private SqlSession sqlSession;
    
        @Before
        public void init() throws IOException {
            //这里特别要注意  我前面没说,Resources是import org.apache.ibatis.io.Resources;包下的
            config = Resources.getResourceAsStream("SqlMapConfig.xml");
            builder = new SqlSessionFactoryBuilder();
            factory = builder.build(config);
    
        }
    
        @After
        public void destroy() throws IOException {
            //关闭流对象
            config.close();
            //关闭sqlSession实例对象
            sqlSession.close();
        }
    
        //查询全部
        @Test
        public void findAll() {
            //获取实例对象SqlSession
            sqlSession = factory.openSession();
            //通过SqlSession获取代理对象
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);
            //调用查询全部方法
            List<Student> all = mapper.findAll();
            for (Student student : all) {
                System.out.println(student);
            }
        }
    
    }
    测试类

    修改后运行一遍即可

    2:查询操作

    //根据学生id查询
        Student findById(Integer id);
    
        //根据学生性别和年龄来查询学生
        List<Student> findBySexAndAge(Map<String,Object> map);
    
        //根据姓名模糊查询
        List<Student> findByLikeName(String name);
    
        //查询学生总数(聚合查询)
        int totalCount();
    抽象类
    <!--根据学生id查询-->
        <select id="findById" parameterType="Integer" resultType="cn.xw.domain.Student">
            select * from student where sid=#{id}
        </select>
    
        <!--根据学生性别和年龄来查询学生-->
        <select id="findBySexAndAge" resultType="cn.xw.domain.Student">
            select * from student where ssex=#{sex} and sage=#{age}
        </select>
    
        <!--根据姓名模糊查询-->
        <select id="findByLikeName" parameterType="String" resultType="cn.xw.domain.Student">
            select * from student where sname like #{name};
        </select>
    
        <!--查询学生总数(聚合查询)-->
        <select id="totalCount" resultType="Integer">
            select count(sid) from student;
        </select>
    StudentDao.xml
    //根据学生id查询
        @Test
        public void findById() {
            sqlSession=factory.openSession();
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);
            Student student = mapper.findById(20);
            System.out.println(student);
        }
    
        //根据学生性别和年龄来查询学生
        @Test
        public void findBySexAndAge() {
            sqlSession = this.sqlSession = factory.openSession();
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);
            Map<String,Object> map=new HashMap<String, Object>();
            map.put("sex","男");
            map.put("age",22);
            List<Student> students = mapper.findBySexAndAge(map);
            for(Student student:students){
                System.out.println(student);
            }
        }
    
        //根据姓名模糊查询
        @Test
        public void findByLikeName() {
            sqlSession = this.sqlSession = factory.openSession();
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);
            List<Student> students = mapper.findByLikeName("%" + "张" + "%");
            for(Student student:students){
                System.out.println(student);
            }
        }
    
        //查询学生总数(聚合查询)
        @Test
        public void totalCount() {
            sqlSession = this.sqlSession = factory.openSession();
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);
            int total = mapper.totalCount();
            System.out.println("总数:"+total);
        }
    测试类

    3 :增删改操作

    //添加学生
        void save(Student student);
    
        //更改学生 姓名 住址
        void update(Student student);
    
        //删除学生
        void delete(Integer id);
    抽象类
     <!--添加学生-->
        <insert id="save" parameterType="cn.xw.domain.Student">
            insert into student (sname,ssex,sage,sscore,smoney,saddress,senrol,tid) values
            (#{sname},#{ssex},#{sage},#{sscore},#{smoney},#{saddress},#{senrol},#{tid});
        </insert>
    
        <!--更改学生 姓名  住址-->
        <update id="update" parameterType="cn.xw.domain.Student">
            update student set sname=#{sname} , saddress=#{saddress} where sid=#{sid}
        </update>
    
        <!--删除学生-->
        <delete id="delete" parameterType="Integer">
            delete from student where sid=#{id}
        </delete>
    StudentDao.xml
     //添加学生
        @Test
        public void save() {
            sqlSession = this.sqlSession = factory.openSession();
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);
            Student student=new Student(0,"老王","男",22,25.5,525.0,"安徽六安","2017-8-8",4);
            mapper.save(student);
            sqlSession.commit();
        }
    
        //更改学生 姓名 住址
        @Test
        public void update() {
            sqlSession = this.sqlSession = factory.openSession();
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);
            Student student=new Student(1,"蚂蚁小哥","",0,0,0,"北京顺义","",0);
            mapper.update(student);
            sqlSession.commit();
        }
    
        //删除学生
        @Test
        public void delete() {
            sqlSession = this.sqlSession = factory.openSession();
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);
            mapper.delete(67);
            sqlSession.commit();
        }
    测试类

    在删除的时候出现这个错误,

    解决:因为我student表是设置外键的,外部的选课表依赖与学生表的主键,所以删除后会出现主键错误,可以删除没有设置选课的学生,比如新添加的数据

    org.apache.ibatis.exceptions.PersistenceException:
    ### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`demo_school`.`course`, CONSTRAINT `course_ibfk_1` FOREIGN KEY (`sid`) REFERENCES `student` (`sid`))

     4:常用其它操作

    ①:问:为什么要在增删改之后要添加事务提交呢?

     我先运行一下上面的使用代理方式完成的增加数据操作,去掉事务

    @Test
    public void save() {
    sqlSession = this.sqlSession = factory.openSession();
    StudentDao mapper = sqlSession.getMapper(StudentDao.class);
    Student student=new Student(0,"老王","男",22,25.5,525.0,"安徽六安","2017-8-8",4);
    mapper.save(student);
    //sqlSession.commit();
    }

      原来如此,默认情况下mybatis是不会自动提交事务,只要在遇到增删改才会出现事务回滚,可是为什么mybatis实现增删改要自动回滚呢?这时候我就去看看源码,翻一下就会看到SqlSessionFactory原来是个接口,他的实现类是DefaultSqlSessionFactory。

     其实我们一般都不设置这个值为true,其实mybatis为我们提高了一系列提交回滚方法

      ②:问 模糊查询为什么传入的参数是  "%"+张+"%" ?

    其实模糊查询的写法有三种,就以我们上面以代理模式的写法运行一下代码

    ####测试类

    @Test
    public void findByLikeName() {
    sqlSession = this.sqlSession = factory.openSession();
    StudentDao mapper = sqlSession.getMapper(StudentDao.class);
    List<Student> students = mapper.findByLikeName("%" + "张" + "%");
    for(Student student:students){
    System.out.println(student);
    }
    }
    ####配置文件
    <!--根据姓名模糊查询-->
    <select id="findByLikeName" parameterType="String" resultType="cn.xw.domain.Student">
    <!--后期传值-->
    select * from student where sname like #{name};
    </select>

     1 普及三种like传值的方法
     2 
     3 第一种:就是上面的这个一种
     4 配置文件sql:select * from student where sname like #{name}
     5 传入的值  :"%"+张+"%"
     6 log日志打印:select * from student where sname like ?
     7 推荐    :使用了占位符 
     8 
    10 第二种:
    11 配置文件sql:select * from student where sname like '%${value}%'
    12 传入的值    :”张“
    13 log日志打印:select * from student where sname like '%张%'
    14 不推荐     :使用固定的值
    15 
    16 第三种:
    17 配置文件sql :select * from student where sname like ”%“#{name}”%18 传入的值    :”张“
    19 log日志打印 :select * from student where sname like "%"?"%"
    20 一般       :使用了占位符 但是看着不舒服

    注:#{} 与 ${} 区别:
    #{}表示一个占位符号
      通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,
      #{}可以有效防止 sql 注入。 #{}可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类
      型值,#{}括号中可以是 value 或其它名称。
    ${}表示拼接 sql 串
      通过${}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换, ${}可以接收简
      单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值,${}括号中只能是 value。

     模糊查询中写${value}的源码分析   org.apache.ibatis.scripting.xmltags.TextSqlNode

    ③:问,可不可以每次添加完数据后获取相应的主键id?

    其实我们在插入数据的时候,一般不指定主键id,因为他是自增长的,所以插入数据只需填写其它字段名称即可,可是,话是这么说,我不指定主键id,那保存到数据库后,我也就不知道当前插入数据的主键id了,那我干脆在插入数据时把主键id的的值也手动写一个就可以解决了,但是别忘了,主键是不允许重复的,所以自己插入主键有可能重复;其实mybatis为我们解决了这些问题。

    其实主要用到的语句是: SELECT LAST_INSERT_ID();

    这个语句代表,当前插入一条新的insert语句并成功执行后,在执行这个语句会返回一个当前插入新数据表的主键id,使用条件:这个表的id字段必须为主键,而且还要设置自增长才可查询结果

     <!--添加学生-->
        <insert id="save" parameterType="Student">
            <!--selectKey 返回插入数据的主键id -->
            <!--keyColumn:mysql数据库主键字段名-->
            <!--keyProperty:实体bean对象主键字段名-->
            <!--order:AFTER在插入数据完成之后获取  BEFORE在插入数据之前获取-->
            <selectKey keyColumn="sid" keyProperty="id" order="AFTER" resultType="Integer">
                select last_insert_id();
            </selectKey>
            insert into student (sname,ssex,sage,sscore,smoney,saddress,senrol,tid) values
            (#{name},#{sex},#{age},#{score},#{money},#{address},#{enrol},#{tid});
        </insert>
    //添加学生
        @Test
        public void save() {
            sqlSession = this.sqlSession = factory.openSession();
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);
            Student student=new Student(0,"老王","男",22,25.5,525.0,"安徽六安","2017-8-8",4);
            System.out.println("插入前主键id:"+student.getId()); //插入前主键id:0
            mapper.save(student);
            System.out.println("插入后主键id:"+student.getId()); //插入后主键id:71
            sqlSession.commit();    //提交事务
        }

     5:mybatis与JDBC比较

    ①:JDBC在数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。

      解决:使用mybatis在 SqlMapConfig.xml 中配置数据链接池,使用连接池管理数据库链接。

     ②:使用JDBC写Sql 语句写在代码中造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变 java 代码。

      解决:使用mybatis将 Sql 语句配置在 XXXXDao.xml 文件中与 java 代码分离。

     ③:使用JDBC向 sql 语句传参数麻烦,因为 sql 语句的 where 条件不一定,可能多也可能少,占位符需要和参数对应。

      解决:Mybatis 自动将 java 对象映射至 sql 语句,通过 statement 中的 parameterType 定义输入参数的类型。

     ④:对结果集解析麻烦,sql 变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo 对象解析比较方便。

      解决:Mybatis 自动将 sql 执行结果映射至 java 对象,通过 statement 中的 resultType 定义输出结果的类型。

     三:Mybatis的xxxDao.XML配置文件参数深入

    1:parameterType 参数类型

    在前几章我们用到此参数,它是用来指定传入参数的类型,可以是基本类型、引用类型(如:String,Date),还可以传入实体类类型POJO(如:Student),同时也可以使用包装类(如:Integer),下面就使用了传入引用类型

    <!--根据姓名模糊查询-->
    <select id="findByLikeName" parameterType="String" resultType="cn.xw.domain.Student">
    select * from student where sname like #{name};
    </select>

     这里首先说一下,parameterType接收的参数必须是全限定类名(如:java.lang.String),因为这样才能准确找到此类,但是你们肯定吐槽我说,为什么上面的写String却可以呢?其实mybatis在加载时已经把一些常用的数据类型注册了别名,从而使我们在使用的时候不用书写包名,但是我们自己定义的实体类是没有注册别名的,所以必须写全限定类名,如何注册别名在后面会说明(使用typeAliases)

     1                                      基本类型
     2 别名            类型
     3 _byte         byte
     4 _long         long
     5 _short        short
     6 _int            int
     7 _integer      int
     8 _double      double
     9 _float         float
    10 _boolean    boolean
    11                                     包装类型
    12 string         String
    13 byte           Byte
    14 long            Long
    15 short          Short
    16 int              Integer
    17 integer       Integer
    18 double        Double
    19 float           Float
    20 boolean      Boolean
    21 date           Date
    22 decimal      BigDecimal   
    23 bigdecimal  BigDecimal
    24 object         Object
    25 map           Map
    26 hashmap    HashMap
    27 list             List
    28 arraylist     ArrayList
    29 collection   Collection
    30 iterator      Iterator                                     
    mybatis为我们注册的数据类型  参考mybatis官网文档13页

     2:POJO包装对象

     POJO(Plain Ordinary Java Object)是简单java对象,其实也可以看成是一个普通的javabean对象,主要是用来包装实体类对象的,假设现在要查询一个数据,但是它用到了2个实体类对象的数据,回顾之前的说,查询数据或者添加数据只能传入一个Object对象,所以我们没法传入2个实体类,那我们就干脆在定义一个POJO对象,把2个对象封装到里面就ok了,封装后就把这个对象传入

    //这就是一个POJO
    class QueryVO{
      private Student student;   //Student对象
      private Teacher teacher;   //Teacher对象  
    }
        <select id="findxxx" parameterType="cn.xw.domain.QueryVo" resultType="cn.xw.domain.xxxx">
            select * from xxxxx where sname=#{student.name} and tname=#{teacher.name}
        </select>

    上面2段时伪代码,大家都知道直接传入javaBean对象可以直接获取其属性如#{name}、#{age}....可是传入POJO对象后无法直接传入属性,你不可能直接#{student}传入,因为系统不知道你传入student里面的那个具体属性,所以就#{student.name}这样传入

    3:resultType配置结构类型

    resultType 属性可以指定结果集的类型,它支持基本类型和实体类类型。我们在前面的 CRUD 案例中已经对此属性进行过应用了。需要注意的是,它和 parameterType 一样,如果注册过类型别名的,可以直接使用别名。没有注册过的必须使用全限定类名。例如:我们的实体类此时必须是全限定类名,因为我们没有指明具体别名
    <!--根据学生id查询-->
    <select id="findById" parameterType="Integer" resultType="cn.xw.domain.Student">
    select * from student where sid=#{id}
    </select>

     大家要注意了,这里就是重点了,其实我们之前一直都是Bean实体类的属性和mysql里面的属性一一对应,所以每次查询数据后,mybatis会根据指定的resultType配置结构里面的实体类字段挨个映射(映射是通过set方法 ,所以要默认的set方法,不要随意更改,比如setName,去除set后把Name变小写,就然后就提取出了name名称,mybatis会拿name和mysql里面的字段匹配,匹配上就映射出值到bean对象的name里),

    但是现在来个例子它们双方不匹配

    StudentDao.xml文件
    <!--
    查询全部学生--> <select id="findAll" resultType="cn.xw.domain.Student"> select * from student; </select>
    //测试类
    //
    查询全部 @Test public void findAll() { //获取实例对象SqlSession sqlSession = factory.openSession(); //通过SqlSession获取代理对象 StudentDao mapper = sqlSession.getMapper(StudentDao.class); //调用查询全部方法 List<Student> all = mapper.findAll(); for (Student student : all) { System.out.println(student); } }

     解决方法:苦一点吧,在sql语句上面写别名哈哈

    StudentDao.xml文件
    <!--查询全部学生-->
        <select id="findAll" resultType="cn.xw.domain.Student">
            select sid as id,sname as name , ssex as sex , sage as sage ,sscore as sscore ....  from student;
        </select>

    4:resultMap结果类型

      resultMap 标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装。在 select 标签中使用 resultMap 属性指定引用即可。同时 resultMap 可以实现将查询结果映射为复杂类型的 pojo,比如在查询结果映射对象中包括 pojo 和 list 实现一对一查询和一对多查询。(1对1、1对多、多对多后面详细会介绍)
      接着上面一个问题说,我们之前解决的方法是使用别名,那么如果字段有好几十个,手动写别名会特别类的,那有没有更方便的方法呢,肯定是有的,resultMap就可以很好的帮我们解决
    没写resultMap结果类型参数的代码:
        <!--查询全部学生-->
        <select id="findAll" resultType="cn.xw.domain.Student">
            select * from student;
        </select>
        //查询全部
        @Test
        public void findAll() {
            //获取实例对象SqlSession
            sqlSession = factory.openSession();
            //通过SqlSession获取代理对象
            StudentDao mapper = sqlSession.getMapper(StudentDao.class);
            //调用查询全部方法
            List<Student> all = mapper.findAll();
            for (Student student : all) {
                System.out.println(student);
            }
        }

     使用resultMap来解决字段名称不一样

    <!--名称字段不一样可以使用这个映射名称对应-->
        <resultMap id="studentMapper" type="cn.xw.domain.Student">
            <id column="sid" property="id"></id>
            <result column="sname" property="name"></result>
            <result column="ssex" property="sex"></result>
            <result column="sage" property="age"></result>
            <result column="sscore" property="score"></result>
            <result column="smoney" property="money"></result>
            <result column="saddress" property="address"></result>
            <result column="senrol" property="enrol"></result>
            <result column="tid" property="tid"></result>
        </resultMap>
        <!--查询全部学生    要使用上面的resultMap才可以-->
        <select id="findAll" resultMap="studentMapper">
            select * from student;
        </select>

    针对resultMap参数介绍:

    resultMap标签属性
    type属性:指定实体类的全限定类目,如cn.xw.domain.Student,,如果给定了别名可以使用别名,后面会介绍
    id 属性:给当前的resultMap的标签定义一个id名称,别的select标签要使用可以在里面指定resultMap的具体id名称

    resultMap标签内部属性
    id 标签:用于指定字段里面的主键字段
    result标签:用来指定非主键的普通字段
    association标签:一对一 后面会说
    collection标签:一对多  后面会说
    column属性:用来指定数据库列名
    property属性:用来指明实体类bean的属性名称
    javaType属性:指定当前实体类属性的类型 可以不写 因为类型我们已经给出了
    jdbcType属性:指定当前mysql字段的类型  可以不写 因为类型我们已经给出了

    四:SqlMapConfig.xml配置详解

    1 :配置内容顺序

     2:properties属性介绍

    properties常用配置于数据源的四大配置说明driver、url、username、password,使用此配置有2种方法,我接下来就和大家介绍一下

    第一种方法:直接在properties标签内部定义,我想说这不是多此一举吗,反正都是在这个文件里面定义,还无形中增加了代码

    <!--配置资源信息-->
        <properties>
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/demo_school"/>
            <property name="username" value="root"/>
            <property name="password" value="123"/>
        </properties>
        <environments default="mysql">
            <environment id="mysql">
                <transactionManager type="JDBC"></transactionManager>
                <dataSource type="POOLED">
                    <!--四大配置 driver驱动 url数据库地址 username/password账号密码-->
                    <property name="driver" value="${driver}"/>
                    <property name="url" value="${url}"/>
                    <property name="username" value="${username}"/>
                    <property name="password" value="${password}"/>
                </dataSource>
            </environment>
        </environments>

    第二种方法:引用外部文件

    <!--这个是mysql连接配置  properties.properties-->
    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/demo_school
    username=root
    password=123
    <!--配置资源信息-->
        <properties resource="properties.properties"></properties>
        <environments default="mysql">
            <environment id="mysql">
                <transactionManager type="JDBC"></transactionManager>
                <dataSource type="POOLED">
                    <!--四大配置 driver驱动 url数据库地址 username/password账号密码-->
                    <property name="driver" value="${driver}"/>
                    <property name="url" value="${url}"/>
                    <property name="username" value="${username}"/>
                    <property name="password" value="${password}"/>
                </dataSource>
            </environment>
        </environments>

    说到这里我就要和大家说一下properties标签的属性

    resource属性:这个是用来指定配置文件下的路径如cn/xw/dao/StudentDao.xml,以斜杠分割的是资源路径,而且这个属性也还是个相对路径

     url属性:这个全称是Uniform resource Locator统一资源定位符,它以协议、主机、端口、URI四部分组成

     

     3:typeAliases类型别名

    我们在前面讲mybatis支持默认别名的,我们可以采用自定义的别名来定义自己的对象

    <!--配置类型别名-->
        <typeAliases>
            <!--单个别名定义  如果domain里面有20个类要定义,使用这个方法会特别繁琐-->
            <typeAlias type="cn.xw.domain.Student" alias="student"></typeAlias>
            <!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->
            <package name="cn.xw.domain"/>
            <package name="其它包"/>
        </typeAliases>

    4:mappers映射器配置

    其实映射器有三种写法

    ①:<mapper resource=" " />
      使用相对于类路径的资源

    如:<mapper resource="com/xw/dao/StudentDao.xml" />

    ②:<mapper class=" " />
      使用 dao 接口类路径

    如:<mapper class="com.xw.dao.StudentDao"/>
    注意:此种方法要求 dao 接口名称和 dao 映射文件名称相同,且放在同一个目录中。


    ③: <package name=""/>
      注册指定包下的所有 mapper 接口

    如:<package name="cn.xw.dao"/>
    注意:此种方法要求 dao 接口名称和 dao 映射文件名称相同,且放在同一个目录中。

    五:总结

    通过这篇博文大家可以基本的使用mybatis对数据的简单的操作,如数据的增删改查等等,但是涉及到多表操作、连接池、缓存、注解开发还没有具体说明,这个会再接下来的几篇博文为大家一一介绍

  • 相关阅读:
    2019-2020-1 20191312《信息安全专业导论》第十二周学习总结
    GPG
    2019-2020-1 20191312《信息安全专业导论》第十一周学习总结
    wireshark
    ssh
    Nmap
    2019-2020-1 《python程序设计》20192428魏来 综合实践报告
    20192428 魏来 2019-2020《Python程序设计》 实验三 报告
    20192428 实验二《Python程序设计》实验报告
    20192428 实验一《Python程序设计》实验报告
  • 原文地址:https://www.cnblogs.com/antLaddie/p/12760295.html
Copyright © 2020-2023  润新知