mybatis——开发dao层

1 SqlSession生命周期

  1. sqlsession:方法级别
  2. sqlsessionFactory:全局范围(应用级别)
  3. sqlsessionFactoryBuilder:方法级别

1.1 SqlSessionFactoryBuilder

SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,因为SqlSession是通过SqlSessionFactory生产,所以可以将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围(即方法体内局部变量)。

1.2 SqlSessionFactory

SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory。

1.3 SqlSession

SqlSession中封装了对数据库的操作,如查询、插入、更新、删除等。通过SqlSessionFactory创建SqlSession,而SqlSessionFactory是通过SqlSessionFactoryBuilder进行创建的。
SqlSession是一个面向程序员的接口,SqlSession中定义了数据库操作方法,所以SqlSession作用是操作数据库,并且SqlSession对象要存储数据库连接、事务和一级缓存结构等。
每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它是线程不安全的(多线程访问系统,当多线程同时使用一个SqlSession对象时会造成数据冲突问题)。由于SqlSession对象是线程不安全的,因此它的最佳使用范围是请求或方法范围(也可说为SqlSession的最佳使用场合是在方法体内作为局部变量来使用),绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
打开一个SqlSession,使用完毕就要关闭它。通常把这个关闭操作放到finally块中以确保每次都能执行关闭。如下:

SqlSession session = sqlSessionFactory.openSession();
try {
   
} finally {
    session.close();
}

2 原始Dao开发方式

原始Dao开发方法需要程序员自己编写Dao接口和Dao实现类。

2.1 UserMapper.xml和SqlMapConfig.xml

UserMapper.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="user">
    <select id="findUserById" parameterType="int" resultType="User">
        SELECT * from user WHERE id=#{id}
    </select>

    <insert id="addUser" parameterType="User">
        <!-- selectKey将主键返回,需要再返回 -->
        <selectKey keyProperty="id" order="AFTER" resultType="int">
            select LAST_INSERT_ID()
        </selectKey>
        INSERT INTO user(username,password,email,phone,address)
        VALUES (#{username},#{password},#{email},#{phone},#{address})
    </insert>
    
    <select id="getUserByName" parameterType="String" resultType="user">
        SELECT * FROM user where username=#{username}
    </select>
</mapper>

SqlMapConfig.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="db.properties"></properties>
    <typeAliases>
        <typeAlias type="pojo.User" alias="user"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}" />
                <property name="url" value="${db.url}" />
                <property name="username" value="${db.username}" />
                <property name="password" value="${db.password}" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml" />
    </mappers>
</configuration>

2.2 UserDao和UserDaoImp

public interface UserDao {
    User findUserbyId(int id);
    void addUser(User user);
    List<User> getUserByName(String username);
}

UserDaoImpl.java:

public class UserDaoImp implements UserDao {
    private SqlSessionFactory sqlSessionFactory;
    public UserDaoImp(SqlSessionFactory sqlSessionFactory){
        this.sqlSessionFactory =sqlSessionFactory;
    }
    public User findUserbyId(int id) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = sqlSession.selectOne("findUserById",id);
        return user;
    }
    public void addUser(User user) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        sqlSession.insert("addUser", user);
        sqlSession.commit();
        if (sqlSession!=null)
            sqlSession.close();
    }

    public List<User> getUserByName(String username) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<User> users =sqlSession.selectList("getUserByName",username);
        return users;
    }
}

2.2 测试

public class UserDaoTest {
    private SqlSessionFactory sqlSessionFactory = null; // 工厂对象一般在我们的系统中是单例的
    @Before
    public void init() throws IOException {
        // 第一步,创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        // 第二步,加载配置文件
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 第三步,创建SqlSessionFactory对象
        sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
    }
    @Test
    public void testFindUserById(){
        UserDao userDao = new UserDaoImp(sqlSessionFactory);
        User u =userDao.findUserbyId(1);
        System.out.println(u);
    }

    @Test
    public void testAddUser(){
        UserDao userDao = new UserDaoImp(sqlSessionFactory);
        User user = new User("小xiao小", "111", "1@com", "123", "sasas");
        userDao.addUser(user);
    }

    @Test
    public void testGetUserByName(){
        UserDao userDao = new UserDaoImp(sqlSessionFactory);
        List<User> users =userDao.getUserByName("小小");
        for (User u:users)
            System.out.println(u);
    }
}

2.3 原始Dao开发方式所带来的问题

从以上UserDaoImpl类的代码可看出原始Dao开发存在以下问题:

  1. dao接口实现类方法中存在大量的重复代码,这些重复的代码就是模板代码。
    模板代码为:
    先创建sqlsession
    再调用sqlsession的方法
    再提交sqlsession
    再关闭sqlsession
    设想能否将这些代码提取出来,这可大大减轻程序员的工作量。
  2. 调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不得于开发维护。
  3. 调用sqlsession方法时传入的变量,由于sqlsession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。

3 Mapper动态代理开发方式

设计模式——动态代理
Mapper接口开发方法只需要程序员编写Mapper接口,由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。

3.1 Mapper接口开发需要遵循以下规范:

Mapper.xml文件中的namespace与mapper接口的类路径相同,即namespace必须是接口的全限定名。
Mapper接口方法名和Mapper.xml中定义的每个statement的id相同。
Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同。
Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。

3.2 编写Mapper.xml(映射文件)

我们可在config源码目录下新建一个mapper的普通文件夹,该文件夹专门用于存放映射文件。然后在该文件夹下创建一个名为mapper.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="mapper.UserMapper">
    <select id="findUserById" parameterType="int" resultType="User">
        SELECT * from user WHERE id=#{id}
    </select>

    <insert id="addUser" parameterType="User">
        <!-- selectKey将主键返回,需要再返回 -->
        <selectKey keyProperty="id" order="AFTER" resultType="int">
            select LAST_INSERT_ID()
        </selectKey>
        INSERT INTO user(username,password,email,phone,address)
        VALUES (#{username},#{password},#{email},#{phone},#{address})
    </insert>

    <select id="getUserByName" parameterType="String" resultType="user">
        SELECT * FROM user where username=#{username}
    </select>
</mapper>

3.3 编写Mapper接口

创建一个Mapper接口——UserMapper.java:

public interface UserMapper {
    User findUserById(Integer id);
    void addUser(User user);
    List<User> getUserByName(String username);
}

3.4 SqlMapConfig.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="db.properties"></properties>
    <typeAliases>
       <!-- <typeAlias type="pojo.User" alias="user"/>-->
        <package name="pojo"/>
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}" />
                <property name="url" value="${db.url}" />
                <property name="username" value="${db.username}" />
                <property name="password" value="${db.password}" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
       <!--<mapper resource="mapper/UserMapper.xml" />-->
        <package name="mapper"/>
    </mappers>
</configuration>

3.5 UserMapperTest

public class UserMapperTest {
    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void creatSqlSessionFactory() throws IOException {
        String resource = "SqlMapConfig.xml";
        InputStream in = Resources.getResourceAsStream(resource);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
    }

    @Test
    public void testFindUserById(){
        // 和Spring整合后就省略了
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 获得代理对象(到时候就只需要通过Spring容器拿到UserMapper接口的代理对象就可以了)
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User u =userMapper.findUserById(1);
        System.out.println(u);
    }

    @Test
    public void testAddUser(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = new User("小xiao小", "111", "1@com", "123", "sasas");
        userMapper.addUser(user);
    }

    @Test
    public void testGetUserByName(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> users =userMapper.getUserByName("小小");
        for (User u:users)
            System.out.println(u);
    }
}

4 小结

4.1 selectOne和selectList

动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。

4.2 namespace

mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用pojo包装对象或map对象,保证dao的通用性。

4.3 SqlMapConfig.xml配置文件

SqlMapConfig.xml文件中配置的内容和顺序如下:

properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
properties(属性)

如果不按顺序配置,xml会报错

4.3.1 数据库配置

数据配置属性方法一

<properties>
    <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
    <property name="jdbc.url" value="jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8"/>
</properties>

数据配置属性方法二

db.driver=com.mysql.jdbc.Driver
db.url = jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8
db.username = root
db.password=root
<properties resource="db.properties"></properties>
<dataSource type="POOLED">
	<property name="driver" value="${db.driver}" />
	<property name="url" value="${db.url}" />
	<property name="username" value="${db.username}" />
	<property name="password" value="${db.password}" />
</dataSource>

加载properties顺序

  1. 先读取在properties元素体内定义的属性
  2. 然后会读取properties元素中resource或url加载的属性,它会覆盖已读取的同名属性

4.3.2 typeAliases(类型别名)

1. mybatis支持别名

别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
map Map

2. 自定义别名
2.1配置单个别名

<typeAliases>
    <!-- 单个别名定义  别名不区分大小写 -->
    <typeAlias type="pojo.User" alias="user"/>
</typeAliases>

2.2配置扫描包的别名

<!-- 配置pojo的别名 -->
<typeAliases>
    <!-- 批量别名定义,扫描包的形式创建别名,别名就是类名,且不区分大小写 -->
    <package name="pojo"/>
</typeAliases>

4.3.3 SqlMapConfig.xml文件加载mapper.xml文件

<mappers>
        <!-- 资源路径加载 -->
       <mapper resource="mapper/UserMapper.xml" />
       <!-- 类路径加载 -->
        <!--注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。-->
        <mapper class="mapper/UserMapper.xml"/>
        <!--注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。-->
        <package name="mapper"/>
</mappers>

在maven项目中,当把UserMapper.xml放在java源文件路径下,而没有放在resources资源路径下,程序可能会抛出Invalid bound statement (not found): mapper.UserMapper.getUserByName异常
解决方法
在pom.xml中加上如下配置

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
    </resources>
</build>

猜你喜欢

转载自blog.csdn.net/ccoran/article/details/84991960