美文网首页
Mybatis(二)

Mybatis(二)

作者: 醉了俗身醒了初心 | 来源:发表于2019-05-05 18:36 被阅读0次

Mybatis 环境搭建步骤

第一步:创建maven工程
第二步:导入坐标
第三步:编写必要代码(实体类和持久层接口)
第四步:编写 SqlMapConfig.xml
第五步:编写映射配置文件
第六步:编写测试类

自定义流程分析

图片1.png

根据id查询

在持久层接口中, 添加findById方法

/**
* 根据 id 查询
* @param userId
* @return
*/
User findById(Integer userId

在用户的映射配置文件中配置

<!-- 根据 id 查询 -->
<select id="findById" resultType="com.itheima.domain.User" parameterType="int">
select * from user where id = #{uid}
</select>
细节:
1. resultType 属性:
    用于指定结果集的类型。
2. parameterType 属性
    用于指定传入参数的类型。
3. sql 语句中使用#{}字符:
    它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的    数据。
    具体的数据是由#{}里面的内容决定的。
4. #{}中内容的写法:
    由于数据类型是基本类型,所以此处可以随意写

在测试类添加测试

public class MybastisCRUDTest {
      private InputStream in ;
      private SqlSessionFactory factory;
      private SqlSession session;
      private IUserDao userDao;
@Test
public void testFindOne() {
    //执行操作
    User user = userDao.findById(41);
    System.out.println(user);
}
@Before//在测试方法执行之前执行
public void init()throws Exception {
     //1.读取配置文件
    in = Resources.getResourceAsStream("SqlMapConfig.xml");
    //2.创建构建者对象
    SqlSessionFactoryBuilder builder = new SqlSessionFactor
    //3.创建 SqlSession 工厂对象
    factory = builder.build(in);
    //4.创建 SqlSession 对象
    session = factory.openSession();
    //5.创建 Dao 的代理对象
    userDao = session.getMapper(IUserDao.class)
}
@After//在测试方法执行完成之后执行
public void destroy() throws Exception{
    session.commit();
    //7.释放资源
    session.close();
    in.close();
}

保存操作

在持久层接口中添加新增方法

/**
* 保存用户
* @param user
* @return 影响数据库记录的行数
*/
int saveUser(User user);

在用户的映射配置文件中

<!-- 保存用户-->
<insert id="saveUser" parameterType="com.itheima.domain.User">
    insert into user(username,birthday,sex,address) 
    values(#{username},#{birthday},#{sex},#{address})
</insert>

细节:
1. parameterType 属性:
    代表参数的类型,因为我们要传入的是一个类的对象,所以类型就写类的全名称。
2. sql 语句中使用#{}字符:
    它代表占位符,相当于原来 jdbc 部分所学的?,都是用于执行语句时替换实际的数据。
    具体的数据是由#{}里面的内容决定的。
3. #{}中内容的写法:
    由于我们保存方法的参数是 一个 User 对象,此处要写 User 对象中的属性名称。
    它用的是 ognl 表达式。
4. ognl 表达式:
    它是 apache 提供的一种表达式语言,全称是:
    Object Graphic Navigation Language 对象图导航语言
    它是按照一定的语法格式来获取数据的。
    语法格式就是使用 #{对象.对象}的方式
5. #{user.username}
    它会先去找 user 对象,然后在 user 对象中找到 username 属性,并调用
    getUsername()方法把值取出来。但是我们在 parameterType 属性上指定了实体    类名称,所以可以省略 user.
    而直接写 username。

添加测试类中的测试方法

@Test
public void testSave(){
    User user = new User();
    user.setUsername("modify User property");
    user.setAddress("北京市顺义区");
    user.setSex("男");
    user.setBirthday(new Date());
    System.out.println("保存操作之前:"+user);
    //5.执行保存方法
    userDao.saveUser(user);
    System.out.println("保存操作之后:"+user);
}

打开 Mysql 数据库发现并没有添加任何记录,原因是什么?
这一点和 jdbc 是一样的,我们在实现增删改时一定要去控制事务的提交,那么在 mybatis 中如何控制事务
提交呢?
可以使用:session.commit();来实现事务提交。加入事务提交后的代码如下:

@After//在测试方法执行完成之后执行
public void destroy() throws Exception{
    session.commit();
    //7.释放资源
    session.close();
    in.close();
}

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

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

<insert id="saveUser" parameterType="USER">
    <!-- 配置保存时获取插入的 id -->
    <selectKey keyColumn="id" keyProperty="id" resultType="int">
         select last_insert_id();
    </selectKey>
    insert into user(username,birthday,sex,address) 
    values(#{username},#{birthday},#{sex},#{address})
</insert>

用户更新

在持久层接口中添加更新方法

/**
* 更新用户
* @param user
* @return 影响数据库记录的行数
*/
int updateUser(User user);

在用户的映射配置文件中配置

<!-- 更新用户 -->
<update id="updateUser" parameterType="com.itheima.domain.User">
    update user set username=#{username},birthday=#{birthday},sex=#{sex},
    address=#{address} where id=#{id}
</update>

加入更新的测试方法

@Test
public void testUpdateUser()throws Exception{
    //1.根据 id 查询
    User user = userDao.findById(52);
    //2.更新操作
    user.setAddress("北京市顺义区");
    int res = userDao.updateUser(user);
    System.out.println(res);
}

用户删除

在持久层接口中添加删除方法

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

在用户的映射配置文件中配置

<!-- 删除用户 -->
<delete id="deleteUser" parameterType="java.lang.Integer">
    delete from user where id = #{uid}
</delete>

加入删除的测试方法

@Test
public void testDeleteUser() throws Exception {
    //6.执行操作
    int res = userDao.deleteUser(52);
    System.out.println(res);
}

用户模糊查询

在持久层接口中添加模糊查询方法

/**
* 根据名称模糊查询
* @param username
* @return
*/
List<User> findByName(String username);

在用户的映射配置文件中配置

<!-- 根据名称模糊查询 -->
<select id="findByName" resultType="com.itheima.domain.User"         parameterType="String">
     select * from user where username like #{username}
</select>

加入模糊查询的测试方法

@Test
 public void testFindByName(){
      //5.执行查询一个方法
     List<User> users = userDao.findByName("%王%");
     for(User user : users){
         System.out.println(user);
     }
 }

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

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

第一步:修改 SQL 语句的配置,配置如下:
<!-- 根据名称模糊查询 -->
<select id="findByName" parameterType="string" resultType="com.itheima.domain.User">
     select * from user where username like '%${value}%'
</select>

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

/**
* 测试模糊查询操作
 */
@Test
public void testFindByName(){
    //5.执行查询一个方法
    List<User> users = userDao.findByName("王");
    for(User user : users){
      System.out.println(user);
    }
}

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

{}与${}的区别

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

查询使用聚合函数

在持久层接口中添加模糊查询方法

/**
* 查询总记录条数
* @return
*/
int findTotal();

在用户的映射配置文件中配置

<!-- 查询总记录条数 -->
<select id="findTotal" resultType="int">
    select count(*) from user;
</select>

加入聚合查询的测试方法

@Test
public void testFindTotal() throws Exception {
    //6.执行操作
    int res = userDao.findTotal();
    System.out.println(res);
}

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 定义输出结果的
类型。

parameterType 配置参数

使用说明

我们在上一章节中已经介绍了 SQL 语句传参,使用标签的 parameterType 属性来设定。该属性的取值可以
是基本类型,引用类型(例如:String 类型),还可以是实体类类型(POJO 类)。同时也可以使用实体类的包装
类,本章节将介绍如何使用实体类的包装类作为参数传递。

注意事项

基 本 类 型 和 String 我 们 可 以 直 接 写 类 型 名 称 , 也 可 以 使 用 包 名 . 类 名 的 方 式 , 例 如 :
java.lang.String。
实体类类型,目前我们只能使用全限定类名。
究其原因,是 mybaits 在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名,
而我们的是实体类并没有注册别名,所以必须写全限定类名。在今天课程的最后一个章节中将讲解如何注册实体类
的别名。
在 mybatis 的官方文档的说明(第 19 页)


2019-05-05_184026.png

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

传递 pojo 包装对象

开发中通过 pojo 传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查
询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。
Pojo 类中包含 pojo。
需求:根据用户名查询用户信息,查询条件放到 QueryVo 的 user 属性中。

编写 QueryVo

public class QueryVo implements Serializable {
    private User user;
      public User getUser() {
        return user;
      }
      public void setUser(User user) {
      this.user = user;
    }
}

编写持久层接口

public interface IUserDao {
  /**
  * 根据 QueryVo 中的条件查询用户
  * @param vo
  * @return
  */
  List<User> findByVo(QueryVo vo);
}

持久层接口的映射文件

<!-- 根据用户名称模糊查询,参数变成一个 QueryVo 对象了 -->
<select id="findByVo" resultType="com.itheima.domain.User"
 parameterType="com.itheima.domain.QueryVo">
    select * from user where username like #{user.username};
</select>

测试包装类作为参数

@Test
public void testFindByQueryVo() {
    QueryVo vo = new QueryVo();
    User user = new User();
    user.setUserName("%王%");
    vo.setUser(user);
    List<User> users = userDao.findByVo(vo);
    for(User u : users) {
      System.out.println(u);
    }
}

Mybatis 的输出结果封装

resultType 配置结果类型

resultType 属性可以指定结果集的类型,它支持基本类型和实体类类型。
我们在前面的 CRUD 案例中已经对此属性进行过应用了。
需要注意的是,它和 parameterType 一样,如果注册过类型别名的,可以直接使用别名。没有注册过的必须
使用全限定类名。例如:我们的实体类此时必须是全限定类名(今天最后一个章节会讲解如何配置实体类的别名)
同时,当是实体类名称是,还有一个要求,实体类中的属性名称必须和查询语句中的列名保持一致,否则无法
实现封装。

Dao 接口

/**
* 查询总记录条数
* @return
*/
int findTotal();

/**
* 查询所有用户
* @return
*/
List<User> findAll();

映射配置

<!-- 查询总记录条数 -->
<select id="findTotal" resultType="int">
    select count(*) from user;
</select>

<!-- 配置查询所有操作 -->
<select id="findAll" resultType="com.itheima.domain.User">
    select * from user
</select>

修改实体类

实体类代码如下:(此时的实体类属性和数据库表的列名已经不一致了)

public class User implements Serializable {
  private Integer userId;
  private String userName;
  private Date userBirthday;
  private String userSex;
  private String userAddress;
  public Integer getUserId() {
    return userId;
  }
  public void setUserId(Integer userId) {
    this.userId = userId;
  }
  public String getUserName() {
    return userName;
  }
  public void setUserName(String userName) {
    this.userName = userName;
  }
  public Date getUserBirthday() {
    return userBirthday;
  }
  public void setUserBirthday(Date userBirthday) {
    this.userBirthday = userBirthday;
  }
  public String getUserSex() {
    return userSex;
  }
  public void setUserSex(String userSex) {
    this.userSex = userSex;
  }
  public String getUserAddress() {
    return userAddress;
  }
  public void setUserAddress(String userAddress) {
    this.userAddress = userAddress;
  }
  @Override
  public String toString() {
    return "User [userId=" + userId + ", userName=" + userName + ", userBirthday="
    + userBirthday + ", userSex="
    + userSex + ", userAddress=" + userAddress + "]";
  }
}

修改映射配置

使用别名查询
<!-- 配置查询所有操作 -->
<select id="findAll" resultType="com.itheima.domain.User">
  select id as userId,username as userName,birthday as userBirthday,
  sex as userSex,address as userAddress from user
</select>

思考:
如果我们的查询很多,都使用别名的话写起来岂不是很麻烦,有没有别的解决办法呢?
请看下一小节

resultMap 结果类型

定义 resultMap

<!-- 建立 User 实体和数据库表的对应关系
  type 属性:指定实体类的全限定类名
  id 属性:给定一个唯一标识,是给查询 select 标签引用用的。
-->
<resultMap type="com.itheima.domain.User" id="userMap">
  <id column="id" property="userId"/>
  <result column="username" property="userName"/>
  <result column="sex" property="userSex"/>
  <result column="address" property="userAddress"/>
  <result column="birthday" property="userBirthday"/>
</resultMap>
id 标签:用于指定主键字段
result 标签:用于指定非主键字段
column 属性:用于指定数据库列名
property 属性:用于指定实体类属性名称

映射配置

<!-- 配置查询所有操作 -->
<select id="findAll" resultMap="userMap">
  select * from user
</select>

测试结果

@Test
public void testFindAll() {
  List<User> users = userDao.findAll();
  for(User user : users) {
  System.out.println(user);
  }
}

SqlMapConfig.xml配置文件

SqlMapConfig.xml 中配置的内容和顺序

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

在使用 properties 标签配置时,我们可以采用两种方式指定属性配置。

第一种 在SqlMapConfig.xml文件中, 添加properties标签

<properties>
  <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
  <property name="jdbc.url" value="jdbc:mysql://localhost:3306/eesy"/>
  <property name="jdbc.username" value="root"/>
  <property name="jdbc.password" value="1234"/>
</properties>

第二种 在 classpath 下定义 db.properties 文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy
jdbc.username=root
jdbc.password=1234

properties 标签配置

<!-- 配置连接数据库的信息
  resource 属性:用于指定 properties 配置文件的位置,要求配置文件必须在类路径下
  resource="jdbcConfig.properties"
  url 属性:
  URL: Uniform Resource Locator 统一资源定位符
  http://localhost:8080/mystroe/CategoryServlet URL
  协议 主机 端口 URI
  URI:Uniform Resource Identifier 统一资源标识符
  /mystroe/CategoryServlet
  它是可以在 web 应用中唯一定位一个资源的路径
-->
<properties url=
  file:///D:/IdeaProjects/mybatisCRUD/src/main/resources/jdbcConfig.properties">
</properties>

此时我们的 dataSource 标签就变成了引用上面的配置

<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>

typeAliases(类型别名)
在前面我们讲的 Mybatis 支持的默认别名,我们也可以采用自定义别名方式来开发

自定义别名:

在 SqlMapConfig.xml 中配置:
<typeAliases>
  <!-- 单个别名定义 -->
  <typeAlias alias="user" type="com.itheima.domain.User"/>
  <!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->
  <package name="com.itheima.domain"/>
  <package name="其它包"/>
</typeAliases>

mappers(映射器) <mapper resource=" " />

使用相对于类路径的资源
如:<mapper resource="com/itheima/dao/IUserDao.xml" />

<mapper class=" " />

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

<package name=""/>

注册指定包下的所有 mapper 接口
如:<package name="cn.itcast.mybatis.mapper"/>
注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。

相关文章

网友评论

      本文标题:Mybatis(二)

      本文链接:https://www.haomeiwen.com/subject/ajbhoqtx.html