Java框架学习:MyBatis之传统开发与Mapper代理开发、获取自增长主键的值

环境搭建

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="db.properties"/>

    <typeAliases>
        <package name="com.young.model"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${db.driverClassName}"/>
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="com/young/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

db.properties配置文件

db.url=jdbc:mysql://localhost:3306/mybatis_db?serverTimezone=UTC
db.username=root
db.password=123
db.driverClassName=com.mysql.jdbc.Driver

UserMapper.xml映射文件

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.young.mapper.UserMapper">
    <select id="selectAll" resultType="User">
        select * from user
    </select>
</mapper>

实体类

public class User {
    private Integer id;
    private String username;
    private String password;

    public User() {
    }

    public User(Integer id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

   //以下省略了所有成员变量的get和set方法......
   
}

使用MyBatis的DAO传统开发

编写工具类进行读取配置文件、构建SqlSessionFactory实例操作

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.io.InputStream;

public class SessionFactoryUtil {

    private static Logger log=Logger.getLogger(SessionFactoryUtil.class);

    private static SqlSessionFactory sessionFactory;

    private static final String CONFIG_FILE_NAME="mybatis-config.xml";

    private SessionFactoryUtil() {
    }

    static{
        try {
            log.debug("开始加载MyBatis配置文件");
            InputStream in = Resources.getResourceAsStream(CONFIG_FILE_NAME);
            sessionFactory = new SqlSessionFactoryBuilder().build(in);
        } catch (IOException e) {
            log.error("加载MyBatis配置文件失败");
            System.exit(0);
        }
    }

    public static SqlSessionFactory getSessionFactory(){
        return sessionFactory;
    }
}

DAO层中的接口IUserMapper

import com.young.model.User;

import java.util.List;

public interface IUserMapper {
    List<User> selectAll();

    void insert(User user);

    void update(User user);

    void delete(Integer id);
}

UserMapper类

import com.young.model.User;
import com.young.utils.SessionFactoryUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class UserMapper implements IUserMapper {
    public List<User> selectAll(){
        SqlSession session=null;
        try {
            session = SessionFactoryUtil.getSessionFactory().openSession();
            List<User> list = session.selectList("com.young.mapper.UserMapper.selectAll");
            return list;
        } finally {
            session.close();
        }
    }

    public void insert(User user){
        SqlSession session=null;
        try {
            session = SessionFactoryUtil.getSessionFactory().openSession();
            session.insert("com.young.mapper.UserMapper.insert",user);
            session.commit();
        } finally {
            session.close();
        }
    }

    public void update(User user){
        SqlSession session=null;
        try {
            session = SessionFactoryUtil.getSessionFactory().openSession();
            session.update("com.young.mapper.UserMapper.update",user);
            session.commit();
        } finally {
            session.close();
        }
    }

    public void delete(Integer id){
        SqlSession session=null;
        try {
            session = SessionFactoryUtil.getSessionFactory().openSession();
            session.delete("com.young.mapper.UserMapper.update",id);
            session.commit();
        } finally {
            session.close();
        }
    }
}

如果使用MyBatis框架这样来编写DAO层中的类,每一个实现类方法中都需要进行try-finally操作,造成代码冗余,且在代码中嵌入了大量的 sqlid。

Mapper代理开发

代码实现

在传统开发开发中,在DAO层中编写接口以及实现类,编写mapper.xml映射文件。而在Mapper代理开发中不需要编写接口的实现类,只需要按照MyBatis的规则对接口和Mapper文件编写,MyBatis会使用代理,自动生成接口的实现类(代理类)。
注:MyBatis框架采用的是JDK代理模式

Mapper代理开发的规则:
1.Mapper.xml映射文件中的namespace必须和接口的全路径保持一致。
2.Mapper.xml映射文件中的方法名必须和接口中的方法名保持一致。
3.因为接口中的方法是可以被重载的,所以Mapper.xml映射文件中Sql-id对应的返回值类型和参数类型需要与接口中保持一致。
4.需要将Mapper接口交给MyBaits框架

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="com.young.mapper.UserMapper">
    <select id="selectAll" resultType="User">
        select * from user
    </select>

    <insert id="insert" parameterType="User">
        insert into user values (#{id},#{username},#{password})
    </insert>

    <update id="update" parameterType="User">
        update user set username=#{username} where id=#{id}
    </update>

    <delete id="delete" parameterType="java.lang.Integer">
        delete from user where id=#{id}
    </delete>
</mapper>

UserMapper接口

import com.young.model.User;

import java.util.List;

public interface UserMapper {
    List<User> selectAll();

    void insert(User user);

    void update(User user);

    void delete(Integer id);
}

在mybatis-config.xml配置文件中,我们将UserMapper.xml映射文件交给MyBatis框架,现在我们值需要将UserMapper接口交给MyBatis框架。

<!--注意"."和"/"的使用-->
<mappers>
   <mapper class="com.young.mapper.UserMapper"/>
</mappers>

测试类

import com.alibaba.fastjson.JSON;
import com.young.mapper.UserMapper;
import com.young.model.User;
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.Test;

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

public class TestMapper {
    @Test
    public void testMapper() throws IOException {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        SqlSession session = sessionFactory.openSession();
        //通过getMapper方法获取UserMapper接口的代理类实例
        UserMapper userMapper = session.getMapper(UserMapper.class);
        List<User> list = userMapper.selectAll();
        System.out.println(JSON.toJSONString(list,true));
        session.close();
    }
}

测试其他方法,省略获取创建SqlSession实例和userMapper接口代理类实例的过程。
insert方法

User user = new User(6,"fff","666");
userMapper.insert(user);
session.commit();
session.close();

update方法

User user = new User(2,"efg","345");
userMapper.update(user);
session.commit();
session.close();

delete方法

userMapper.delete(5);
session.commit();
session.close();
获取自增长主键的值

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="db.properties"/>

    <typeAliases>
        <package name="com.young.model"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${db.driverClassName}"/>
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
       <mapper class="com.young.mapper.UserMapper"/>
    </mappers>
</configuration>

前提:数据库支持主键自增长,且主键增加的自增长约束

第一种方式
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="com.young.mapper.UserMapper">
	<!--useGeneratedKeys:表示是否使用自增长主键的值
        keyProperty:将主键值与Java对象的属性对应-->
    <insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
        insert into user(username,password) values (#{username},#{password})
    </insert>
</mapper>

测试方法

public class TestMapper {
    @Test
    public void testMapper() throws IOException {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        SqlSession session = sessionFactory.openSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);

        User user = new User();
        user.setUsername("abc");
        user.setPassword("123");
        userMapper.insert(user);
        System.out.println("id-->"+user.getId());
        
        session.commit();
        
        session.close();
    }
}

执行插入操作后,数据库会返回插入的数据的主键值并返回给测试方法中User对象的id属性。

第二种方式:
MySQL数据库中提供了内置函数last_insert_id(),用于查询最后一条插入数据的id(主键)的值,只适用于自增长主键。
UserMapper.xml映射文件

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.young.mapper.UserMapper">
    <insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
    	<!--keyProperty:将主键值与Java对象的属性对应
            resultType:返回值的类型
            order:执行的顺序,"AFTER"表示在insert语句执行之后执行;"BEFORE"表示在insert语句执行之前执行-->
        <selectKey keyProperty="id" resultType="int" order="AFTER">
            select last_insert_id()
        </selectKey>
        insert into user(username,password) values (#{username},#{password})
    </insert>
</mapper>

测试方法

@Test
public void testMapper() throws IOException {
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
    SqlSession session = sessionFactory.openSession();
    UserMapper userMapper = session.getMapper(UserMapper.class);

    User user = new User();
    user.setUsername("abc");
    user.setPassword("123");
    userMapper.insert(user);
    System.out.println("id-->"+user.getId());
    
    session.commit();
    session.close();
}

在insert语句执行之后会查询最后一条插入数据的id值,并将结果返回给测试方法中的User类对象的id属性。
控制台输出的日志:

[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/24 10:58:22 DEBUG [insert] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==>  Preparing: insert into user(username,password) values (?,?) 
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/24 10:58:22 DEBUG [insert] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Parameters: abc(String), 123(String)
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/24 10:58:22 DEBUG [insert] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - <==    Updates: 1
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/24 10:58:22 DEBUG [insert!selectKey] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==>  Preparing: select last_insert_id() 
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/24 10:58:22 DEBUG [insert!selectKey] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Parameters: 
[ org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) ] 2020/05/24 10:58:22 DEBUG [insert!selectKey] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - <==      Total: 1

通过日志可知,插入语句先执行,"select last_insert_id()"语句后执行。

如果数据库不支持主键自增长,可以使用如下方式(该方式也适用于支持主键自增长的数据库)。
获取UUID作为表中主键值,这样表中主键值就不会重复
uuid()方法返回的是一个字符串,所以需要将主键id的类型更改为varchar类型,否则会报错。在此处以列username为例

<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
   <selectKey keyProperty="username" resultType="string" order="BEFORE">
        select uuid()
    </selectKey>
    insert into user(username,password) values (#{username},#{password})
</insert>

测试方法

@Test
public void testMapper() throws IOException {
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
    SqlSession session = sessionFactory.openSession();
    UserMapper userMapper = session.getMapper(UserMapper.class);

    User user = new User();
    user.setPassword("123");
    userMapper.insert(user);
    System.out.println("username-->"+user.getUsername());
    
    session.commit();
    session.close();
}

先执行uuid()方法获取一个UUID,然后将该字符串赋值给测试方法中的User类对象的username属性,然后执行插入语句。

Oracle数据库不支持主键自增长,推荐主键的id值为sequence,可以使用函数查询主键值,并设置到属性中(了解即可)。

查询结果无法对应自定义类

如果查询的结果来源于多张表,或者使用了函数,那么结果可能无法与自定义对象对应。如果出现这种情况,那么可以使用Map集合来存储查询到的结果。
UserMapper接口中的方法

Map<String,User> selectById(Integer id);

UserMapper.xml配置文件

<!--因为方法返回值为Map,所以resultType也要设置为Map集合-->
<!--HashMap集合也可以在resultType中直接指定,无需写完整的包名-->
<select id="selectById" resultType="hashmap">
      select * from user where id=#{id}
</select>

测试方法

Map<String, User> map = userMapper.selectById(1);
System.out.println(JSON.toJSONString(map,true));

运行结果
查询出的列名作为键

{
	"password":"111",
	"id":1,
	"username":"aaa"
}

注意:当返回结果为Map集合是,只会调用selectOne()方法,如果查询出多条数据,会出现异常。

可以使用@MapKey注解将查询结果的某一列的值作为Map集合的键,值为对象
UserMapper接口中的方法

//如果此处的列名不存在,那么返回的Map集合的键为"null"
@MapKey("id")
Map<String,User> selectById(Integer id);

运行测试方法的运行结果为:

{1:{
		"password":"111",
		"id":1,
		"username":"aaa"
	}
}
方法需要传入多个参数

当存在一个方法需要传入多个参数
接口中的方法

User select(Integer id,String username);

UserMapper.xml配置文件

<mapper namespace="com.young.mapper.UserMapper">
    <select id="select" resultType="User">
          select * from user where id=#{id} and username=#{username}
    </select>
</mapper>

测试方法

@Test
public void testMapper() throws IOException {
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
    SqlSession session = sessionFactory.openSession();
    UserMapper userMapper = session.getMapper(UserMapper.class);

    User user = userMapper.select(1, "aaa");
    System.out.println(JSON.toJSONString(user));
    session.close();
}

运行测试方法后会出现如下异常

Caused by: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2]

所以在传入多个参数时需要用param1、param2…代替属性值

<select id="select" resultType="User">
      select * from user where id=#{param1} and username=#{param2}
</select>

如果不想使用param1、param2,也可以使用@Param注解标注方法中的参数

User select(@Param("id")Integer id,@Param("username")String username);

补充:
如果parameterType的类型为Map,即参数是一个Map集合,那么和自定义类传参方式一样,在#{}中写键名。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值