使用Mybatis ibatis Hibernate

什么是MyBatis?

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射(ORM)。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

安装

要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。

如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>

在spring的环境下使用mybatis

新建一个maven项目,为其添加pom

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.softeem</groupId>
    <artifactId>spring-mybatis-test</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <java.version>1.8</java.version>

        <spring.version>5.2.0.RELEASE</spring.version>
        <mybatis.version>3.5.4</mybatis.version>
        <mybatis-spring.version>2.0.4</mybatis-spring.version>
        <hikaricp.version>3.4.2</hikaricp.version>
        <hsqldb.version>2.5.0</hsqldb.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>${mybatis.version}</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>${mybatis-spring.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.20</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>
    </dependencies>
</project>

SqlSessionFactory

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

如果单独使用mybatis,我们需要通过配置文件的形式来创建SqlSessionFactory对象

<?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>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

当然还有很多可以添加的额外配置内容

我们现在的运行环境是spring容器,而容器要管理所有参与业务的bean,自然SqlSeesionFactory对象也可以委托给spring容器来创建

AppConfig.java

@Bean
    public DataSource createDataSource(
            @Value("${url}") String url,
            @Value("${user}")String user,
            @Value("${driver}")String driver,
            @Value("${password}")String password){
        DruidDataSource ds = new DruidDataSource();
        ds.setUrl(url);
        ds.setUsername(user);
        ds.setPassword(password);
        ds.setDriverClassName(driver);
        return ds;
    }

    @Bean
    public SqlSessionFactoryBean createSqlSessionFactory(@Autowired DataSource dataSource) throws IOException {
        SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
        sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:/mapper/*.xml"));
        sqlSessionFactory.setDataSource(dataSource);
        return sqlSessionFactory;
    }

    @Bean
    public PlatformTransactionManager createTxManager(@Autowired DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

我们引入了数据源,事物管理器。由于SqlSessionFactory是一个接口,而Spring为了能够和Mybatis整合,故而实现了该接口,实现类的名称为SqlSessionFactoryBean, 我们通过@Bean将其加载至容器中

详见 createSqlSessionFactory方法

半ORM和优秀灵活的SQL

Mybatis最大的特点应该就是它只是一个半ORM的框架,我们依旧需要编写SQL语句,它简化了我们最终将结果集resultSet映射到对象的这一步操作。Mybatis相较于JDBC,在编写SQL上进行一些优化,提供了一些例如可以帮助我们实现动态SQL的标签库

mybatis 示例

 @Select("select * from user")
    public List<User> listUsers();

    @Select("select * from user where id = #{value}")
    public User getUserById(int id);

使用Mybatis

  • 引入相关的pom支持
  • 在Configuration(AppConfig.java)中配置SqlSessionFactory到容器,该对象需要传入一个DataSource数据源
  • 编写Mapper接口,定义对应的方法。
  • 添加注解,写入SQL语句
  • 在Configuration类中添加注解@MapperScan去扫描mybatis的mapper
  • 运行
@Configuration
@ComponentScan
@PropertySource("jdbc.properties")
@MapperScan("com.softeem.mapper")
public class AppConfig {

注解方式

是不是非常容易,但是这样的mybatis无法满足我们去实现更复杂的需求,或者说,如果我们需求过于复杂,使用这种方式并不是最好的解决方案,因为代码不够优雅?

比如,如果我们要实现比较复杂的查询逻辑,如判断参数是否为空,为空则查询否则不带入该条件,那么我们的Mapper接口可能会使这样

@Select("<script>" +
        "select * from user" +
        " where 1=1 " +
        "<if test='username!=null'>" +
        "and username = #{username}" +
        "</if>" +
        "<if test='password!=null'>" +
        "and password = #{password}" +
        "</if>" +
        "</script>")
public List<User> listUsersByUserNameOrPassword(@Param("username") String username,@Param("password") String password);

这样子可读性太差了,所以非常不推荐在有这种需求的情况下使用这种方式,这样写是没有灵魂的!

配置文件方式

当有这种需求时,我们更适合使用配置文件的形式来实现

mybatis最传统的用法,也是编写对应mapper的配置文件

mybatis配置文件示例

<?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.softeem.mapper.BookMapper">
  <resultMap id="BaseResultMap" type="com.softeem.entity.Book">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="cover" jdbcType="VARCHAR" property="cover" />
    <result column="title" jdbcType="VARCHAR" property="title" />
    <result column="author" jdbcType="VARCHAR" property="author" />
    <result column="date" jdbcType="VARCHAR" property="date" />
    <result column="press" jdbcType="VARCHAR" property="press" />
    <result column="abs" jdbcType="VARCHAR" property="abs" />
    <result column="cid" jdbcType="INTEGER" property="cid" />
  </resultMap>
  <sql id="Base_Column_List">
    id, cover, title, author, `date`, press, `abs`, cid
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from book
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from book
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.softeem.entity.Book" useGeneratedKeys="true">
    insert into book (cover, title, author,
      `date`, press, `abs`, cid
      )
    values (#{cover,jdbcType=VARCHAR}, #{title,jdbcType=VARCHAR}, #{author,jdbcType=VARCHAR},
      #{date,jdbcType=VARCHAR}, #{press,jdbcType=VARCHAR}, #{abs,jdbcType=VARCHAR}, #{cid,jdbcType=INTEGER}
      )
  </insert>
  <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.softeem.entity.Book" useGeneratedKeys="true">
    insert into book
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="cover != null">
        cover,
      </if>
      <if test="title != null">
        title,
      </if>
      <if test="author != null">
        author,
      </if>
      <if test="date != null">
        `date`,
      </if>
      <if test="press != null">
        press,
      </if>
      <if test="abs != null">
        `abs`,
      </if>
      <if test="cid != null">
        cid,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="cover != null">
        #{cover,jdbcType=VARCHAR},
      </if>
      <if test="title != null">
        #{title,jdbcType=VARCHAR},
      </if>
      <if test="author != null">
        #{author,jdbcType=VARCHAR},
      </if>
      <if test="date != null">
        #{date,jdbcType=VARCHAR},
      </if>
      <if test="press != null">
        #{press,jdbcType=VARCHAR},
      </if>
      <if test="abs != null">
        #{abs,jdbcType=VARCHAR},
      </if>
      <if test="cid != null">
        #{cid,jdbcType=INTEGER},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.softeem.entity.Book">
    update book
    <set>
      <if test="cover != null">
        cover = #{cover,jdbcType=VARCHAR},
      </if>
      <if test="title != null">
        title = #{title,jdbcType=VARCHAR},
      </if>
      <if test="author != null">
        author = #{author,jdbcType=VARCHAR},
      </if>
      <if test="date != null">
        `date` = #{date,jdbcType=VARCHAR},
      </if>
      <if test="press != null">
        press = #{press,jdbcType=VARCHAR},
      </if>
      <if test="abs != null">
        `abs` = #{abs,jdbcType=VARCHAR},
      </if>
      <if test="cid != null">
        cid = #{cid,jdbcType=INTEGER},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.softeem.entity.Book">
    update book
    set cover = #{cover,jdbcType=VARCHAR},
      title = #{title,jdbcType=VARCHAR},
      author = #{author,jdbcType=VARCHAR},
      `date` = #{date,jdbcType=VARCHAR},
      press = #{press,jdbcType=VARCHAR},
      `abs` = #{abs,jdbcType=VARCHAR},
      cid = #{cid,jdbcType=INTEGER}
    where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>

准确的说,这个叫映射文件,用来和mapper相对应,mapper接口中定义的方法,在该文件中进行实现。上面的例子基于idea插件的自动生成,同时为我们生成了基本的增删改查语句

注解好还是配置文件更好?每个人都有自己的看法,在springboot大火的当下,我们显然应该跟上时代潮流拥抱注解,但是复杂需求还是配置文件更好怎么办?

我全都要!!

小孩子才做选择!

同时使用注解和配置文件就好

  • 保留原有的mapperScan注解,Spring容器会继续扫描mybatis的注解
  • 对mybatis的核心对象SQLSessionFactory进行配置
 @Bean
    public SqlSessionFactoryBean createSqlSessionFactory(@Autowired DataSource dataSource) throws IOException {
        SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
        sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:/mapper/*.xml"));
        sqlSessionFactory.setDataSource(dataSource);
        return sqlSessionFactory;
    }

动态 SQL

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach
if

使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:

<select id="findActiveBlogWithTitleLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

这条语句提供了可选的查找文本功能。如果不传入 “title”,那么所有处于 “ACTIVE” 状态的 BLOG 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果(细心的读者可能会发现,“title” 的参数值需要包含查找掩码或通配符字符)。

如果希望通过 “title” 和 “author” 两个参数进行可选搜索该怎么办呢?首先,我想先将语句名称修改成更名副其实的名称;接下来,只需要加入另一个条件即可。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>
choose、when、otherwise

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG(这可能是管理员认为,与其返回大量的无意义随机 Blog,还不如返回一些由管理员挑选的 Blog)。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>
trim、where、set

前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 “state = ‘ACTIVE’” 设置成动态条件,看看会发生什么。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE
  <if test="state != null">
    state = #{state}
  </if>
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:

SELECT * FROM BLOG
WHERE

这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:

SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’

这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。

MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>


set user=#{user},pwd=#{pwd}    (1,23,45)
<trim prefix="(" suffixOverrides="," suffix=")">
</trim>
<trim prefix="set" suffixOverrides=",">
    <if test="user!=null"> 
    	user = #{user}
    </if>
      <if test="pwd!=null"> 
    	pwd = #{pwd}
    </if>
</trim>

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

来看看与 set 元素等价的自定义 trim 元素吧:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

注意,我们覆盖了后缀值设置,并且自定义了前缀值。

foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

<select id="selectPostIn" resultType="domain.blog.Post"> 
 SELECT *  FROM POST P  WHERE ID in  
 <foreach item="item" index="index" collection="list"      
 open="(" separator="," close=")">        
 #{item}  
 </foreach></select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

至此,我们已经完成了与 XML 配置及映射文件相关的讨论。下一章将详细探讨 Java API,以便你能充分利用已经创建的映射配置。

public interface Mapper {
  @Lang(MyLanguageDriver.class)
  @Select("SELECT * FROM BLOG")
  List<Blog> selectBlog();
}

关于ORM

ORM一般称为对象关系映射,将数据库中的关系转移到JavaBean,在表与表的关系中,大多数情况下都是多对一/一对多关系,多对多,像这种关系我们可以使用外键来建立联系,且永远由多方来维护关系

如何将这种模式带到java中呢,从而实现数据库关系到javabean关系的转换

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于微信小程序的家政服务预约系统采用PHP语言和微信小程序技术,数据库采用Mysql,运行软件为微信开发者工具。本系统实现了管理员和客户、员工三个角色的功能。管理员的功能为客户管理、员工管理、家政服务管理、服务预约管理、员工风采管理、客户需求管理、接单管理等。客户的功能为查看家政服务进行预约和发布自己的需求以及管理预约信息和接单信息等。员工可以查看预约信息和进行接单。本系统实现了网上预约家政服务的流程化管理,可以帮助工作人员的管理工作和帮助客户查询家政服务的相关信息,改变了客户找家政服务的方式,提高了预约家政服务的效率。 本系统是针对网上预约家政服务开发的工作管理系统,包括到所有的工作内容。可以使网上预约家政服务的工作合理化和流程化。本系统包括手机端设计和电脑端设计,有界面和数据库。本系统的使用角色分为管理员和客户、员工三个身份。管理员可以管理系统里的所有信息。员工可以发布服务信息和查询客户的需求进行接单。客户可以发布需求和预约家政服务以及管理预约信息、接单信息。 本功能可以实现家政服务信息的查询和删除,管理员添加家政服务信息功能填写正确的信息就可以实现家政服务信息的添加,点击家政服务信息管理功能可以看到基于微信小程序的家政服务预约系统里所有家政服务的信息,在添加家政服务信息的界面里需要填写标题信息,当信息填写不正确就会造成家政服务信息添加失败。员工风采信息可以使客户更好的了解员工。员工风采信息管理的流程为,管理员点击员工风采信息管理功能,查看员工风采信息,点击员工风采信息添加功能,输入员工风采信息然后点击提交按钮就可以完成员工风采信息的添加。客户需求信息关系着客户的家政服务预约,管理员可以查询和修改客户需求信息,还可以查看客户需求的添加时间。接单信息属于本系统里的核心数据,管理员可以对接单的信息进行查询。本功能设计的目的可以使家政服务进行及时的安排。管理员可以查询员工信息,可以进行修改删除。 客户可以查看自己的预约和修改自己的资料并发布需求以及管理接单信息等。 在首页里可以看到管理员添加和管理的信息,客户可以在首页里进行家政服务的预约和公司介绍信息的了解。 员工可以查询客户需求进行接单以及管理家政服务信息和留言信息、收藏信息等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值