Mybatis框架实现CRUD操作

Mybatis框架实现CRUD操作

为了方便测试,分别增加@Before 和@After,代码如下:


public class MyBatisTest {
    private SqlSession sqlSession;
    private UserMapper userMapper;

    /***
     * 在执行@Test之前执行
     */
    @Before
    public void init(){
        try {
            //1.读取主配置文件(SqlMapConfig.xml),获取配置文件的字节输入流
            InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");

            //2.创建一个SqlSessionFactoryBuilder对象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

            //3.构建SqlSessionFactory(会话工厂对象)
            SqlSessionFactory sqlSessionFactory = builder.build(is);

            //4.打开一个SqlSession,拥有Connection的作用
            sqlSession = sqlSessionFactory.openSession();

            //5.通过SqlSession获取对应Dao的代理类
            userMapper = sqlSession.getMapper(UserMapper.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /***
     * 查询所有
     */
    @Test
    public void testFindAll() throws Exception {
        //调用对应Dao实现数据库增删改查
        List users = userMapper.findAll();
        for (User user : users) {
            System.out.println(user);
        }
    }

    /****
     * @Test执行完成之后执行
     */
    @After
    public void destroy(){
        //7.关闭资源
        sqlSession.close();
    }
}

 一. Mybatis实现用户新增

1. 在UserMapper类中添加新增方法

为了实现新增操作,我们可以在原有入门示例的UserMapper.java类中添加一个用于saveUser()的方法用于用户新增操作。

UserMapper类中新增saveUser()方法,如下:


/**
 * 添加用户
 * @param user
 * @return
 */
int saveUser(User user);

2.在UserMapper.xml文件中加入新增配置

在UserMapper.xml文件中加入新增用户的配置,如下:




    INSERT INTO user(username,birthday,sex,address)VALUES(#{username},#{birthday},#{sex},#{address})

我们可以发现,这个sql语句中使用#{}字符,#{}代表占位符,我们可以理解是原来jdbc部分所学的?,它们都是代表占位符, 具体的值是由User类的username属性来决定的。
parameterType属性:代表参数的类型,因为我们要传入的是一个类的对象,所以类型就写类的全名称。

注意:这种方式要求 ,同时还要求< select>,< insert>,< delete>,< update>这些标签中的id属性一定与代理接口中的方法名相同。

3.添加测试类中的测试方法


/***
 * 保存用户
 */
@Test
public void testSaveUser(){
    User user = new User();
    user.setUsername("张三");
    user.setSex("男");
    user.setBirthday(new Date());
    user.setAddress("深圳");
    userMapper.saveUser(user);
}

4.测试结果如下:


==>  Preparing: INSERT INTO user(username,birthday,sex,address)VALUES(?,?,?,?) 
==> Parameters: 张三(String), 2018-08-01 15:19:50.543(Timestamp), 男(String), 深圳(String)
<==    Updates: 1

打开Mysql数据库发现并没有添加任何记录,原因是什么?

这一点和jdbc是一样的,我们在实现增删改时一定要去控制事务的提交,那么在mybatis中如何控制事务提交呢?

可以使用:session.commit();来实现事务提交。加入事务提交后的代码如下:


/****
 * @Test执行完成之后执行
 */
@After
public void destroy(){
    sqlSession.commit();
    sqlSession.close();
}

5.问题扩展:新增用户id的返回值

新增用户后,同时还要返回当前新增用户的id值,因为id是由数据库的自动增长来实现的,所以就相当于我们要在新增后将自动增长auto_increment的值返回。

Mysql自增主键的返回,配置如下:




    
    
      SELECT LAST_INSERT_ID()
    
    INSERT INTO user(username,birthday,sex,address)VALUES(#{username},#{birthday},#{sex},#{address})

 二. Mybatis实现用户修改

1.在UserMapper类中添加更新方法

为了实现更新操作,我们可以在原有入门示例的UserMapper.java类中添加一个用于updateUser()的方法用于用户更新操作。

UserMapper类中新增updateUser()方法,如下:


/***
 * 修改用户
 * @param user
 * @return
 */
int updateUser(User user);

2.在UerMapper.xml文件中加入更新操作配置

在UserMapper.xml文件中加入更新用户的配置,如下:




    UPDATE user SET username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} WHERE id=#{id}

上面的更新语句中,同样使用#{}代表占位符,比如#{username}代表占位符,将来这个部分使用User对象中的username属性的值来作为sql语句中username字段的值。

3.加入更新的测试方法

/***
 * 修改用户
 */
@Test
public void testUpdateUser(){
    User user = new User();
    user.setUsername("张飞");
    user.setSex("男");
    user.setBirthday(new Date());
    user.setAddress("天津");
    user.setId(61);
    userMapper.updateUser(user);
}

4.测试结果:


==>  Preparing: UPDATE user SET username=?,birthday=?,sex=?,address=? WHERE id=? 
==> Parameters: 张飞(String), 2018-08-01 15:55:02.267(Timestamp), 男(String), 天津(String), 61(Integer)
<==    Updates: 1

  三 . Mybatis实现用户删除

1.在UserMapper类中加入删除方法

在UserMapper类中加入删除deleteUser()方法,用于实现用户的删除。


/**
 * 根据ID删除用户
 * @param id
 * @return
 */
int deleteUser(Integer id);

2.在UserMapper.xml文件中加入删除操作

加入的删除的映射配置信息如下:



    DELETE FROM  user WHERE  id=#{id}


其中的#{id}是占位符,代表参数的值由方法的参数传入进来的。
注意:
1.此处的#{id}中的id其实只是一个形参,所以它的名称是自由定义的,比如定义成#{abc}也是可以的。
2.关于parameterType的取值问题,对于基本类型我们可以直接写成int,short,double…..也可以写成java.lang.Integer。
3.字符串可以写成string,也可以写成java.lang.String

3.加入删除的测试方法

在原有的测试类中加入测试方法,如下:

/***
 * 根据ID删除用户
 */
@Test
public void testDeleteUser(){
    userMapper.deleteUser(61);
}

4.下面是测试的结果:


==>  Preparing: DELETE FROM user WHERE id=? 
==> Parameters: 61(Integer)
<==    Updates: 1

四 . Mybatis实现用户模糊查询第一种方法

现在来实现根据用户名查询用户信息,此时如果用户名想用模糊搜索的话,我就可以想到前面Web课程中所学的模糊查询来实现。

1.在UserMapper类中添加模糊查询方法

可以在UserMapper类中添加一个findUserByUsername()的方法,如下:


/***
 * 模糊查询
 * @param username
 * @return
 */
List findUserByUsername(String username);

2.在UserMapper.xml文件中加入模糊查询的配置

下面在UserMapper.xml文件中加入模糊查询的配置代码,如下:




注意:此时的#{username}中的因为这时候是普通的参数,所以它的起名是随意的,比如我们改成#{abc}也是可以的。

3.加入模糊查询的测试方法

在测试类中加入模糊测试的testFindUserByUsername()方法,如下:


@Test
public void testFindUserByUsername(){
    String name="%张%";
    List users = userMapper.findUserByUsername(name);
    for (User user : users) {
        System.out.println(user);
    }
}

4.在控制台输出的执行SQL语句如下:


==>  Preparing: SELECT * FROM user WHERE username LIKE ? 
==> Parameters: %张%(String)
<==      Total: 1
User{id=42, username='张三', birthday=Fri Mar 02 15:09:37 CST 2018, sex='女', address='北京金燕龙'}

我们在UserMapper.xml配置文件中没有加入%来作为模糊查询的条件,所以在传入字符串实参时,就需要给定模糊查询的标识%。配置文件中的#{username}也只是一个占位符,所以SQL语句显示为“?”。如何将模糊查询的匹配符%写到配置文件中呢?

五. 模糊查询的另一种配置方式

1. 编写UserMapper.xml文件,配置如下:




我们在上面将原来的#{}占位符,改成了${value}。注意如果用模糊查询的这种写法,那么${value}的写法就是固定的,不能写成其它名字。

2. 编写测试方法,如下:


@Test
public void testFindUserByUsername(){
    String name="张";
    List users = userMapper.findUserByUsername(name);
    for (User user : users) {
        System.out.println(user);
    }
}

3. 在控制台输出的执行SQL语句如下:


==>  Preparing: SELECT * FROM user WHERE username LIKE '张' 
==>  Parameters: 
<==      Total: 0

可以发现,我们在程序代码中就不需要加入模糊查询的匹配符%了,这两种方式的实现效果是一样的,但执行的语句是不一样的。

六 . 井{}与${}的区别

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

七 . 模糊查询的${value}源码分析

我们在将模糊查询的匹配符%写到配置文件时,就用到一个固定写法${value},如下匹配:



那么为什么一定要写成${value}呢?我们一起来看TextSqlNode类的源码:

@Override
public String handleToken(String content) {
  Object parameter = context.getBindings().get("_parameter");
  if (parameter == null) {
    context.getBindings().put("value", null);
  } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
    context.getBindings().put("value", parameter);
  }
  Object value = OgnlCache.getValue(content, context.getBindings());
  String srtValue = (value == null ? "" : String.valueOf(value)); // issue #274 return "" instead of "null"
  checkInjection(srtValue);
  return srtValue;
}

 八 . Mybatis默认参数的源码分析

Mybatis默认情况下支持参数写别名,这就导致了我们前面在看基本类型时,既可以写成int,也可以写成java.lang.Integer,默认支持的到底有哪些类型?

从源码角度怎么去找?这些都是支持的默认别名。我们也可以从源码角度来看它们分别都是如何定义出来的。可以参考TypeAliasRegistery.class的源码。


package org.apache.ibatis.type;
public class TypeAliasRegistry {

  public TypeAliasRegistry() {
    registerAlias("string", String.class);

    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);

    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);

    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);

    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);

    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);

    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);

    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);

    registerAlias("ResultSet", ResultSet.class);
  }

  //略.....
}

九 . Mybatis与JDBC编程的比较

1)数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
  解决:在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。
 
2)Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
  解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
 
3)向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
  解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。
 
4)对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
  解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。

这就说明了源码中指定了读取的key的名字就是”value”,所以我们在绑定参数时就只能叫value的名字了。

你可能感兴趣的:(mybatis)