使用IDEA和Mybatis实现CRUD(二)使用了NameSpace特性

使用IDEA和Mybatis实现CRUD(一)
对于上一篇文章中提供2个问题

问题1:

没有使用接口编程,java是面向接口编程语言,对数据库的操作应该定义一些操作接口,如:用户添加、用户删除、用户查询等,调用dao接口完成数据库操作。

解决:

使用接口编程:

  • UserDao.java

    package com.mybatis.dao;
    
    import po.User;
    
    /**
     * Created by JasonTang on 11/12/2016.
    */
    public interface UserDao {
        User findUserById(int id) throws Exception;
    }
  • UserDaoImpl.java

    package com.mybatis.dao;
    
    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.After;
    import org.junit.Before;
    import org.junit.Test;
    import po.User;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * Created by JasonTang on 11/12/2016.
     */
    public class UserDaoImpl implements UserDao{
    
        //这里采用注入的方式来调用SessionFactory
        //因为SessionFactory只需要一个即可(单例模式)
        private SqlSessionFactory sessionFactory;
    
        public UserDaoImpl(SqlSessionFactory sessionFactory) {
            this.sessionFactory = sessionFactory;
        }
        //只实现了查询,其他的都和上一篇文章中的一样,搬过来就可以了
        public User findUserById(int id) throws Exception {
            SqlSession session = null;
            try {
                session = sessionFactory.openSession();
                User user = session.selectOne("test.findUserById", id);
                return user;
            } finally {
                session.close();
            }
        }
    
    }
  • MybatisTest.java

    import com.mybatis.dao.UserDao;
    import com.mybatis.dao.UserDaoImpl;
    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.After;
    import org.junit.Before;
    import org.junit.Test;
    import po.User;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * Created by JasonTang on 09/12/2016.
     */
    public class MybatisTest {
        private SqlSessionFactory sessionFactory;
        @Before
        public void init () throws IOException {
            String resource = "SqlMapConfig.xml";
            InputStream input = Resources.getResourceAsStream(resource);
            sessionFactory = new SqlSessionFactoryBuilder().build(input);
        }
        @Test
        public void testFindUserById() throws Exception {
            //注入SessionFactory
            UserDao ud = new UserDaoImpl(sessionFactory);
            User user = ud.findUserById(1);
            System.out.println(user);
        }
    }
  • 项目结构如下:User.xml不需要修改
    文档结构

问题2:

上面的例子中,在访问sql映射文件中定义的sql时需要调用sqlSession的selectOne方法,并将sql的位置(命名空间+id)和参数传递到selectOne方法中。虽然上边对提出的面向接口编程问题进行解决,但是dao的实现方法中仍然是调用sqlSession的selectOne方法,如果用此方法来实现CRUD的话,重复代码率太高。

设想:

如果能通过方法的返回值来判断调用的方法的话,就比较方便了,例如返回的只有一个对象,则使用selectOne,如果返回是一个list集合,则使用selectList方法。

总结:

还好,Mybatis引入了NameSpace的概念,此时只需要将mapper.xml和dao接口进行映射,dao接口和mapper.xml需要有一个对应关系。 mapper.xml中namespace的作用:将mapper.xml(映射文件),mapper.java(接口文件)关联。具体怎么关联我们先不去关系,现在需要关心的时,已经建立好了映射关系之后,怎么去实现通过判断返回值来自动选择不同的方法。
官方给出的文档中,有个Mappers:
6.Using Mappers

<T> T getMapper(Class<T> type)

While the various insert, update, delete and select methods above are powerful, they are also very verbose, not type safe and not as helpful to your IDE or unit tests as they could be. We’ve already seen an example of using Mappers in the Getting Started section above.

Therefore, a more common way to execute mapped statements is to use Mapper classes. A mapper class is simply an interface with method definitions that match up against the SqlSession methods.

The following example class demonstrates some method signatures and how they map to the SqlSession.

源码如下:

public interface AuthorMapper {
// (Author) selectOne("selectAuthor",5);
Author selectAuthor(int id);
// (List<Author>) selectList(“selectAuthors”)
List<Author> selectAuthors();
// (Map<Integer,Author>) selectMap("selectAuthors", "id")
@MapKey("id")
Map<Integer, Author> selectAuthors();
// insert("insertAuthor", author)
int insertAuthor(Author author);
// updateAuthor("updateAuthor", author)
int updateAuthor(Author author);
// delete("deleteAuthor",5)
int deleteAuthor(int id);
}

In a nutshell, each Mapper method signature should match that of the SqlSession method that it’s associated to, but without the String parameter ID. Instead, the method name must match the mapped statement ID.

In addition, the return type must match that of the expected result type for single results or an array or collection for multiple results. All of the usual types are supported, including: Primitives, Maps, POJOs and JavaBeans.

NOTE Mapper interfaces do not need to implement any interface or extend any class. As long as the method signature can be used to uniquely identify a corresponding mapped statement.

NOTE Mapper interfaces can extend other interfaces. Be sure that you have the statements in the appropriate namespace when using XML binding to Mapper interfaces. Also, the only limitation is that you cannot have the same method signature in two interfaces in a hierarchy (a bad idea anyway).

You can pass multiple parameters to a mapper method. If you do, they will be named by the literal “param” followed by their position in the parameter list by default, for example: #{param1},#{param2} etc. If you wish to change the name of the parameters (multiple only), then you can use the @Param(“paramName”) annotation on the parameter.

You can also pass a RowBounds instance to the method to limit query results

使用映射器

<T> T getMapper(Class<T> type)

上述的各个 insert, update, delete 和 select 方法都很强大,但也有些繁琐,没有类型安全,对于你的 IDE 也没有帮助,还有可能的单元测试。 在上面的入门章节中我们已经看到了一个使用映射器的示例。

下面的示例展示了一些方法签名和它们是如何映射到 SqlSession 的。

public interface AuthorMapper {
// (Author) selectOne(“selectAuthor”,5);
Author selectAuthor(int id);
// (List<Author>) selectList(“selectAuthors”)
List<Author> selectAuthors();
// insert(“insertAuthor”, author)
void insertAuthor(Author author);
// updateAuthor(“updateAuhor”, author)
void updateAuthor(Author author);
// delete(“deleteAuthor”,5)
void deleteAuthor(int id);
}

总之,每个映射器方法签名应该匹配相关联的 SqlSession 方法,而没有字符串参数 ID。相反,方法名必须匹配映射语句的 ID

此外,返回类型必须匹配期望的结果类型。所有常用的类型都是支持的,包括:原生类型, Map, POJO 和 JavaBean。

映射器接口不需要去实现任何接口或扩展任何类。只要方法前面可以被用来唯一标识对应的映射语句就可以了。

映射器接口可以扩展其他接口。当使用 XML 来构建映射器接口时要保证在合适的命名空间中有语句。而且,唯一的限制就是你不能在两个继承关系的接口中有相同的方法签名(这也是不好的想法)。

你可以传递多个参数给一个映射器方法。如果你这样做了,默认情况下它们将会以它们在参数列表中的位置来命名,比如: #{1},#{2}等。如果你想改变参数的名称(只在多参数情况下),那么你可以在参数上使用@Param(“paramName”)注解。

你也可以给方法传递一个 RowBounds 实例来限制查询结果。

解决:
  • 同样使用接口来实现,接口定义如下:
  • UserMapperDao.java

    package com.mybatis.dao.mapper;
    
    import po.User;
    
    import java.util.List;
    
    /**
     * Created by JasonTang on 11/12/2016.
     */
    public interface UserMapperDao {
        User findUserById(int id) throws Exception;
        List<User> findUsersByAge(int age) throws Exception;
        void addUser(User user) throws Exception;
        void deleteUserById(int id) throws Exception;
        void updateUser(User user) throws Exception;
    }

    接口定义有如下特点:

    1. Mapper接口方法名和mapper.xml中定义的每个statement的id相同
    2. Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
    3. Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
  • 修改Mapper.xml的namespace,注意namespace(mapper的全限定名)和id的命名

    <?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.mybatis.dao.mapper.UserMapperDao">
        <!--
            resultType使用注意:select查询出来 列名要和resultType指定的类型属性名对应。
            #{},表示占位符,形如?
        -->
        <select id="findUserById" parameterType="int" resultType="po.User">
            SELECT * FROM user WHERE id = #{value}
        </select>
        <select id="findUsersByAge" parameterType="int" resultType="po.User">
            SELECT * FROM user WHERE age = #{value}
        </select>
        <insert id="addUser" parameterType="po.User">
            <selectKey keyProperty="id" order="AFTER" resultType="int">
                SELECT LAST_INSERT_ID()
            </selectKey>
            INSERT INTO user VALUES (#{id},#{username},#{age})
        </insert>
        <delete id="deleteUserById" parameterType="int">
            DELETE FROM user WHERE id = #{value}
        </delete>
        <!-- 这里根据id来更新用户名 -->
        <update id="updateUser" parameterType="po.User">
            UPDATE user SET username=#{username} WHERE id = #{id}
        </update>
    
    </mapper>
  • 通过mapper接口调用statement

    import com.mybatis.dao.mapper.UserMapperDao;
    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.Before;
    import org.junit.Test;
    import po.User;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.List;
    
    /**
     * Created by JasonTang on 09/12/2016.
     */
    public class MybatisTest {
        private SqlSessionFactory sessionFactory;
        @Before
        public void init () throws IOException {
            String resource = "SqlMapConfig.xml";
            InputStream input = Resources.getResourceAsStream(resource);
            sessionFactory = new SqlSessionFactoryBuilder().build(input);
        }
    
        @Test
        public void testFindUserById() throws Exception {
            SqlSession session = sessionFactory.openSession();
            //根据UserMapperDao接口生成代理对象实例
            UserMapperDao userMapper = session.getMapper(UserMapperDao.class);
            //注意这里我没有使用session.selectOne();
            User user = userMapper.findUserById(1);
            System.out.println(user);
        }
        @Test
        public void testFindUsersByAge() throws Exception {
            SqlSession session = sessionFactory.openSession();
            UserMapperDao userMapper = session.getMapper(UserMapperDao.class);
            //注意这里我没有使用session.selectList();
            List<User> users = userMapper.findUsersByAge(12);
            for(User u : users) {
                System.out.println(u);
            }
        }
        @Test
        public void testAddUser() throws Exception {
            SqlSession session = sessionFactory.openSession();
            UserMapperDao userMapper = session.getMapper(UserMapperDao.class);
            User user = new User();
            user.setUsername("大萝卜");
            user.setAge(18);
            userMapper.addUser(user);
            session.commit();
        }
        @Test
        public void testDeleteUserById() throws Exception {
            SqlSession session = sessionFactory.openSession();
            UserMapperDao userMapper = session.getMapper(UserMapperDao.class);
            userMapper.deleteUserById(1);
            session.commit();
        }
        //因为更新时需要id和更新信息,所以直接传入一个User对象即可
        @Test
        public void testUpdateUser() throws Exception {
            SqlSession session = sessionFactory.openSession();
            UserMapperDao userMapper = session.getMapper(UserMapperDao.class);
            User user = new User();
            //需要更新的id号(数据库中已存在的)
            user.setId(9);
            //更新的Username
            user.setUsername("小萝卜");
            userMapper.updateUser(user);
            session.commit();
        }
    }
  • 运行结果:
    数据库:(id int auto increment primary key, username varchar(20), age int)
    数据库
    testFindUserById()的结果
    selectOne
    testFindUsersByAge()的结果
    selectList
    testAddUser()的结果
    addUser
    testDeleteUserById()的结果
    deleteUser
    testUpdateUser()的结果
    updateUser
  • 项目结构如下:
    项目结构
  • 总结:

    使用mapper接口不用写接口实现类即可完成数据库操作,简单方便,此方法为官方推荐方法。

  • getMapper()的源码如下:
    getMapper()源码
    newInstance()源码

  • 使用mapper接口调用必须具备如下条件:

    1. Mapper接口方法名和mapper.xml中定义的每个sql的id相同
    2. Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
    3. Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
    4. Mapper.xml文件中的namespace即是mapper接口的类路径。

    附件:MyBatis-3-User-Guide-Simplified-Chinese(Mybatis-3用户手册中文版)
    链接:http://pan.baidu.com/s/1hrOxwmK 密码:21gl

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值