SpringBoot+Thymeleaf+iBatis开发项目

maven

该项目为maven项目,在我的一台电脑上配置好的项目copy到另一台电脑上,maven貌似需要重新下载maven依赖,maven项目必须联网吗

参考1:Maven installation and using in project without Internet conncetion,该文表示如果在offline状态下用本地的库使用maven,就背离了maven的原则,maven在原则下就是需要联网的,这样才能精确和更好地控制版本。

参考2:How do I configure Maven for offline development?

参考3:maven联网问题,如果真的需要使用本地仓库就这样做。

Thymeleaf

在学习新技术之前,首先需要明白的问题是:

1.它解决了什么问题?
2.它跟与它类似的技术有什么优劣?

学习的一个重大途径是看FAQ!从经常问的问题中寻找答案!

参考thymeleaf FAQ,一些精华:

1.Is Thymeleaf a web framework?

No, it isn’t. It is a template engine.

Template engines play a key role in web frameworks, though, and are one of its most important components as they are in charge of producing the user interface or view layer (usually in XHTML or HTML form).

Spring MVC, Struts or Apache Wicket are examples of web frameworks, whereas JSP, Velocity or FreeMarker are examples of template engines.

2.What types of templates can Thymeleaf process?

Thymeleaf templates can be:

1.HTML (HTML5, XHTML 1.0/1.1, HTML 4)
2.XML
3.TEXT (plain text)
4.JAVASCRIPT (.js files)
5.CSS (.css files)

3.I don’t use Spring at all. Can I still use Thymeleaf?

Absolutely. Thymeleaf offers nice integration with Spring MVC through its SpringStandard dialect (included in the thymeleaf-spring3, thymeleaf-spring4 and thymeleaf-spring5 packages), but Spring integration is completely optional and the Standard dialect is in fact meant to be used without Spring.

4.How does Thymeleaf compare with other template engines like Velocity or FreeMarker?

Both Velocity and FreeMarker are terrific pieces of software, but they approach the templating problem with a philosophy quite different to that of Thymeleaf.

Thymeleaf makes a strong stress on natural templating —allowing templates to be working prototypes, which the other two do not allow—, and its syntax tries to be (arguably) cleaner and more in tune with the current trends in web development. Also, from an architectural standpoint, both Velocity and FreeMarker work as sequential text processors whereas Thymeleaf is based on markup parsing techniques. This allows Thymeleaf to take advantage of interesting features specific to markup-based environments, especially the web.

Anyway, the best way to compare these technologies is to use them yourself and feel which one suits you best.

带着上述问题,我们来看thymeleaf:

首先,它解决什么问题?从FAQ中可以很清楚地看出来,它是一款后端的模板引擎,那么它的大部分功能应该是实现动态填值的功能!

之前的jsp也能实现相应的功能,那么它与jsp比较有哪些优劣呢?

参考:Spring MVC view layer: Thymeleaf vs. JSP

thymeleaf的标签都是以th开头的,如th:href

比较重点可以看看4.Let’s change the page style!这一节:

1.对于jsp:需要先将jsp部署到服务器上然后启动,没有服务器,jsp是不能正常工作的。打开浏览器的dev tools,然后一个一个颜色试,将最终颜色写入css,完成。

2.对于thymeleaf:直接浏览器打开该html文件,打开浏览器的dev tools,然后一个一个颜色试,然后存入css,完成。

上述是跟jsp的优势。

接下来我们来了解下thymeleaf的基础

一、Thymeleaf is very, very extensible, and it allows you to define your own sets of template attributes (or even tags) with the names you want, evaluating the expressions you want in the syntax you want and applying the logic you want. It’s more like a template engine framework.

二、五种Standard Expressions:

1.${…} : Variable expressions.
2.*{…} : Selection expressions.
3.#{…} : Message (i18n) expressions.
4.@{…} : Link (URL) expressions.
5.~{…} : Fragment expressions.

Variable expressions

Variable expressions are executed on the context variables — also called model attributes in Spring jargon.

And you will find them as attribute values or as a part of them, depending on the attribute:

<span th:text="${book.author.name}">

The expression above is equivalent (both in OGNL and SpringEL) to:

((Book)context.getVariable("book")).getAuthor().getName()

当然还有可能有一些高级的用法,比如:

<li th:each="book : ${books}">

Here ${books} selects the variable called books from the context, and evaluates it as an iterable to be used at a th:each loop.

Selection expressions

Selection expressions跟variable expressions很相似,不同点是,Selection expressions作用在a previously selected object,而不是the whole context variables map(variable expressions应该是作用在其上的)。

The object they act on is specified by a th:object attribute,用法:

<div th:object="${book}">
  ...
  <span th:text="*{title}">...</span>
  ...
</div>

上述写法相当于:

{
  // th:object="${book}"
  final Book selection = (Book) context.getVariable("book");
  // th:text="*{title}"
  output(selection.getTitle());
}

Message (i18n) expressions

Message expressions主要是用来国际化的。

it allows us to retrieve locale-specific messages from external sources (.properties files), referencing them by a key and (optionally) applying a set of parameters.

Link (URL) expressions

Link expressions are meant to build URLs and add useful context and session info to them (a process usually called URL rewriting).

举例来说:
1.So for a web application deployed at the /myapp context of your web server, an expression such as:

<a th:href="@{/order/list}">...</a>

也就是:

<a href="/myapp/order/list">...</a>

2.URL也可以带上参数:

<a th:href="@{/order/details(id=${orderId},type=${orderType})}">...</a>

也就是:

<!-- Note ampersands (&) should be HTML-escaped in tag attributes... -->
<a href="/myapp/order/details?id=23&amp;type=online">...</a>

3.当然,链接也可以是绝对的:

<a th:href="@{http://www.mycompany.com/main}">...</a>

Fragment expressions

Fragment expressions are an easy way to represent fragments of markup and move them around templates. Thanks to these expressions, fragments can be replicated, passed to other templates are arguments, and so on.

The most common use is for fragment insertion using th:insert or th:replace,比如:

1.
<div th:insert="~{commons :: main}">...</div>
2.
<div th:with="frag=~{footer :: #main/text()}">
  <p th:insert="${frag}">
</div>

Literals and operations

三、Some basic attributes

th:text的作用是just replaces the body of a tag:

<p th:text="#{msg.welcome}">Welcome everyone!</p>

th:each的用法是做循环嘛:

<li th:each="book : ${books}" th:text="${book.title}">En las Orillas del Sar</li>

在thymeleaf中进行引用时,比如上面的books,我们怎样往里面赋值,然后html界面才能够引用呢?我们可以这样写:

@ModelAttribute("allTypes")
public List<Type> populateTypes() {
    return Arrays.asList(Type.ALL);
}

@ModelAttribute("allFeatures")
public List<Feature> populateFeatures() {
    return Arrays.asList(Feature.ALL);
}

@ModelAttribute("allVarieties")
public List<Variety> populateVarieties() {
    return this.varietyService.findAll();
}

@ModelAttribute("allSeedStarters")
public List<SeedStarter> populateSeedStarters() {
    return this.seedStarterService.findAll();
}

这样我们就可以在界面中引用了,当然也可以使用model.add方法

<tr th:each="sb : ${allSeedStarters}">

更多请参考:Tutorial: Thymeleaf + Spring

关于程序与view的数据交互
参考:Spring MVC and Thymeleaf: how to access data from templates

In a typical Spring MVC application, @Controller classes are responsible for preparing a model map with data and selecting a view to be rendered. This model map allows for the complete abstraction of the view technology and, in the case of Thymeleaf, it is transformed into a Thymeleaf context object (part of the Thymeleaf template execution context) that makes all the defined variables available to expressions executed in templates.

在SpringMVC项目中,通常情况下,@Controller类负责将data传入给model,然后由model将数带入view中。

1.Spring model attributes

Spring MVC calls the pieces of data that can be accessed during the execution of views model attributes . 注意,是在views运行期间能够获取的数据! 也即model数据是跟views相关的。

The equivalent term in Thymeleaf language is context variables.相似术语。

有三种方式向model中加入内容:

1.Add attribute to Model via its addAttribute method:

@RequestMapping(value = "message", method = RequestMethod.GET)
    public String messages(Model model) {
        model.addAttribute("messages", messageRepository.findAll());
        return "message/list";
    }

2.Return ModelAndView with model attributes included:

@RequestMapping(value = "message", method = RequestMethod.GET)
    public ModelAndView messages() {
        ModelAndView mav = new ModelAndView("message/list");
        mav.addObject("messages", messageRepository.findAll());
        return mav;
    }

3.Expose common attributes via methods annotated with @ModelAttribute:

@ModelAttribute("messages")
    public List<Message> messages() {
        return messageRepository.findAll();
    }

In Thymeleaf, these model attributes (or context variables in Thymeleaf jargon) can be accessed with the following syntax: ${attributeName}, where attributeName in our case is messages,比如:

<tr th:each="message : ${messages}">
        <td th:text="${message.id}">1</td>
        <td><a href="#" th:text="${message.title}">Title ...</a></td>
        <td th:text="${message.text}">Text ...</td>
    </tr>

2.Request parameters

比如一个网址:https://example.com/query?q=Thymeleaf+Is+Great!

可以通过param来获取q:

<p th:text="${param.q}">Test</p>

如果q有多值,比如:

https://example.com/query?q=Thymeleaf%20Is%20Great!&q=Really%3F

可以采用数组这样得到:

    <p th:text="${param.q[0] + ' ' + param.q[1]}" th:unless="${param.q == null}">Test</p>

3.Session attributes

In the below example we add mySessionAttribute to session:

@RequestMapping({"/"})
    String index(HttpSession session) {
        session.setAttribute("mySessionAttribute", "someValue");
        return "index";
    }

获取方法:

//方法1
<p th:text="${session.mySessionAttribute}" th:unless="${session == null}">[...]</p>
//方法2
${#session.getAttribute('mySessionAttribute')}

4.ServletContext attributes

The ServletContext attributes are shared between requests and sessions. In order to access ServletContext attributes in Thymeleaf you can use the #servletContext. prefix:

<table>
            <tr>
                <td>My context attribute</td>
                <!-- Retrieves the ServletContext attribute 'myContextAttribute' -->
                <td th:text="${#servletContext.getAttribute('myContextAttribute')}">42</td>
            </tr>
            <tr th:each="attr : ${#servletContext.getAttributeNames()}">
                <td th:text="${attr}">javax.servlet.context.tempdir</td>
                <td th:text="${#servletContext.getAttribute(attr)}">/tmp</td>
            </tr>
        </table>

5.Spring beans

Thymeleaf allows accessing beans registered at the Spring Application Context with the @beanName syntax, for example:

<div th:text="${@urlService.getApplicationUrl()}">...</div>

In the above example, @urlService refers to a Spring Bean registered at your context, e.g.

@Configuration
    public class MyConfiguration {
        @Bean(name = "urlService")
        public UrlService urlService() {
            return () -> "domain.com/myapp";
        }
    }

    public interface UrlService {
        String getApplicationUrl();
    }

数据从程序到view可以走model进行数据交换,那么从view到程序呢?

按我的理解来强答这个问题,client通过http协议,要求访问某个url,server通过定义的controller来进行运算,加载数据进入model,然后model将数据填入html页面中,渲染之后,返回给client。所以可以有model到view这条路,view到程序可以传值可以由client通过http url给server,我想也可以定义所谓的回调函数吧,当然,这也只是猜测。不管怎样,前端数据的改变只有通过http等协议才能传递给后端吧。

iBatis

参考iBatis docs,居然有中文版的docs,感谢翻译人士。

入门

它是一款持久层的框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

1.构建SqlSessionFactory

有两种方式:

1.使用XML

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

MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,可使从 classpath 或其他位置加载资源文件更加容易。

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

XML 配置文件(configuration XML)中包含了对 MyBatis 系统的核心设置,包含获取数据库连接实例的数据源(DataSource)和决定事务作用域和控制方式的事务管理器(TransactionManager)。示例如下:

<?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>

2.使用java代码配置

比如:

DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

注意该例中,configuration 添加了一个映射器类(mapper class)。映射器类是 Java 类,它们包含 SQL 映射语句的注解从而避免了 XML 文件的依赖。不过,由于 Java 注解的一些限制加之某些 MyBatis 映射的复杂性,XML 映射对于大多数高级映射(比如:嵌套 Join 映射)来说仍然是必须的。有鉴于此,如果存在一个对等的 XML 配置文件的话,MyBatis 会自动查找并加载它(这种情况下, BlogMapper.xml 将会基于类路径和 BlogMapper.class 的类名被加载进来)。有对应的xml文件会被优先加载

2.从 SqlSessionFactory 中获取 SqlSession

SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:

SqlSession session = sqlSessionFactory.openSession();
try {
  Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
} finally {
  session.close();
}

上述代码的写法是旧式的,现在有了更新的写法。使用对于给定语句能够合理描述参数和返回值的接口(比如说BlogMapper.class),你现在不但可以执行更清晰和类型安全的代码,而且还不用担心易错的字符串字面值以及强制类型转换。例如:

SqlSession session = sqlSessionFactory.openSession();
try {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  Blog blog = mapper.selectBlog(101);
} finally {
  session.close();
}

3.探究已映射的 SQL 语句

我们来探究一下上述代码到底是怎么正确执行的。

这里给出一个基于 XML 映射语句的示例,它应该可以满足上述示例中 SqlSession 的调用。

<?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="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>

使用方式一:

Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);

注意到这和使用完全限定名调用 Java 对象的方法是相似的。这个命名可以直接映射到在命名空间中同名的 Mapper 类,并在已映射的 select 语句中的名字、参数和返回类型匹配成方法。简而言之,就是配置xml生成了对应的java代码。所以有了使用方式二:

BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);

关于命名空间(namespace):
参考1:What are namespaces?

它的主要作用就是避免同名的方法或者类在被调用时无法区分的问题(不然只有改名)。同时,它也避免了自己项目的代码和第三方代码同名的尴尬。

If we didn’t have namespaces we’d have to (potentially) change a lot of code any time we added a library, or come up with tedious prefixes to make our function names unique. With namespaces, we can avoid the headache of naming collisions when mixing third-party code with our own projects.

对于像 BlogMapper 这样的映射器类(Mapper class)来说,它们的映射的语句可以不需要用 XML 来做,取而代之的是可以使用 Java 注解。映射器类也可以使用java代码配置。比如,上面的 XML 示例可被替换如下:

package org.mybatis.example;
public interface BlogMapper {
  @Select("SELECT * FROM blog WHERE id = #{id}")
  Blog selectBlog(int id);
}

4.作用域(Scope)和生命周期

依赖注入框架可以创建线程安全的、基于事务的 SqlSession 和映射器(mapper)并将它们直接注入到你的 bean 中,因此可以直接忽略它们的生命周期。如果对如何通过依赖注入框架来使用 MyBatis 感兴趣可以研究一下 MyBatis-Spring 或 MyBatis-Guice 两个子项目。

1.SqlSessionFactoryBuilder

SqlSessionFactoryBuilder的作用就是创建SqlSessionFactory,因此该实例的最佳作用域就是方法作用域。你可以重用SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在以保证所有的 XML 解析资源开放给更重要的事情

2.SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。因此 SqlSessionFactory 的最佳作用域是应用作用域。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式

3.SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的管理作用域中,比如 Serlvet 架构中的 HttpSession。如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。下面的示例就是一个确保 SqlSession 关闭的标准模式:

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

4.映射器实例(Mapper Instances)

映射器是创建用来绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,映射器实例的最大作用域是和 SqlSession 相同的,因为它们都是从 SqlSession 里被请求的。尽管如此,映射器实例的最佳作用域是方法作用域。也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可废弃。并不需要显式地关闭映射器实例,尽管在整个请求作用域(request scope)保持映射器实例也不会有什么问题,但是很快你会发现,像 SqlSession 一样,在这个作用域上管理太多的资源的话会难于控制。所以要保持简单,最好把映射器放在方法作用域(method scope)内。下面的示例就展示了这个实践:

SqlSession session = sqlSessionFactory.openSession();
try {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  // do work
} finally {
  session.close();
}

XML 映射配置文件

MyBatis 的配置文件包含了影响 MyBatis 行为甚深的设置(settings)属性(properties)信息。

搞清楚设置和属性信息的区别。

1.properties

这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。例如:

<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

其中的属性就可以在整个配置文件中使用来替换需要动态配置的属性值。比如:

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

动态值由 config.properties 文件中对应的值来替换。

如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载(加载的优先顺序):

1.在 properties 元素体内指定的属性首先被读取。
2.然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
3.最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。

即是,通过方法参数传递的属性>resource/url 属性中指定的配置文件>properties 属性中指定的属性

MyBatis 3.4.2开始,可以使用默认值来进行占位了,不过该属性默认是关闭的。

2.settings

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。具体的settings请参考:settings

3.typeAliases

类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:

套路一:

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

当这样配置时,Blog可以用在任何使用domain.blog.Blog的地方。

套路二,也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>

每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。看下面的例子:

@Alias("author")
public class Author {
    ...
}

套路三,许多常见的 Java 类型内建了相应的类型别名。它们都是大小写不敏感的,需要注意的是由基本类型名称重复导致的特殊处理。具体请参考:typeAliases

4.typeHandlers

无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。

一些标准的java类型都有对应的类型处理器,请参考typeHandlers

当然可以自定义类型处理器,具体做法是:
实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个 JDBC 类型。比如:

// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
    ps.setString(i, parameter);
  }

  @Override
  public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
    return rs.getString(columnName);
  }

  @Override
  public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    return rs.getString(columnIndex);
  }

  @Override
  public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    return cs.getString(columnIndex);
  }
}

配置:

<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>

使用这个的类型处理器将会覆盖已经存在的处理 Java 的 String 类型属性和 VARCHAR 参数及结果的类型处理器。 要注意 MyBatis 不会窥探数据库元信息来决定使用哪种类型,所以你必须在参数和结果映射中指明那是 VARCHAR 类型的字段, 以使其能够绑定到正确的类型处理器上。 这是因为:MyBatis 直到语句被执行才清楚数据类型

通过类型处理器的泛型,MyBatis 可以得知该类型处理器处理的 Java 类型,不过这种行为可以通过两种方法改变:

1.在类型处理器的配置元素(typeHandler element)上增加一个 javaType 属性(比如:javaType=”String”);
2.在类型处理器的类上(TypeHandler class)增加一个 @MappedTypes 注解来指定与其关联的 Java 类型列表。 如果在 javaType 属性中也同时指定,则注解方式将被忽略。

这两种方法应该等价吧。

可以通过两种方式来指定被关联的 JDBC 类型

1.在类型处理器的配置元素上增加一个 jdbcType 属性(比如:jdbcType=”VARCHAR”);
2.在类型处理器的类上(TypeHandler class)增加一个 @MappedJdbcTypes 注解来指定与其关联的 JDBC 类型列表。 如果在 jdbcType 属性中也同时指定,则注解方式将被忽略。

这两种方法也应该等价吧。

上述两种类型(java类型,JDBC类型)非常重要。当决定在ResultMap中使用某一TypeHandler时,此时java类型是已知的(从结果类型中获得),但是JDBC类型是未知的。Mybatis使用javaType=[TheJavaType], jdbcType=null的组合来选择一个TypeHandler

配置以查找类型处理器:

<!-- mybatis-config.xml -->
<typeHandlers>
  <package name="org.mybatis.example"/>
</typeHandlers>

注意在使用自动检索(autodiscovery)功能的时候,只能通过注解方式来指定 JDBC 的类型。

5.对象工厂(objectFactory)

MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。

6.插件(plugins)

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

1.Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
2.ParameterHandler (getParameterObject, setParameters)
3.ResultSetHandler (handleResultSets, handleOutputParameters)
4.StatementHandler (prepare, parameterize, batch, update, query)

通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定了想要拦截的方法签名即可

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  public Object intercept(Invocation invocation) throws Throwable {
    return invocation.proceed();
  }
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  public void setProperties(Properties properties) {
  }
}

配置:

<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行低层映射语句的内部对象

7.配置环境(environments)

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者共享相同 Schema 的多个生产数据库, 想使用相同的 SQL 映射。

不过要记住:尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一

必须遵守的原则:每个数据库对应一个 SqlSessionFactory 实例

环境元素定义了如何配置环境。

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <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>

注意这里的关键点:
1.默认的环境 ID(比如:default=”development”)。
2.每个 environment 元素定义的环境 ID(比如:id=”development”)。
3.事务管理器的配置(比如:type=”JDBC”)。
4.数据源的配置(比如:type=”POOLED”)。

事务管理器
在 MyBatis 中有两种类型的事务管理器(也就是 type=[JDBC|MANAGED]”),如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器, 因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

数据源
有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”

UNPOOLED这个数据源的实现只是每次被请求时打开和关闭连接。虽然一点慢,它对在及时可用连接方面没有性能要求的简单应用程序是一个很好的选择。 不同的数据库在这方面表现也是不一样的,所以对某些数据库来说使用连接池并不重要,这个配置也是理想的。

POOLED这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这是一种使得并发 Web 应用快速响应请求的流行处理方式

JNDI– 这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。

8.databaseIdProvider

MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。 为支持多厂商特性只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider 即可:

<databaseIdProvider type="DB_VENDOR" />

9.映射器(mappers)

既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。但是首先我们需要告诉 MyBatis 到哪里去找到这些语句Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件(iBatis没有像Spring那样在自动查找方面的造诣啊)。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。例如:

<!-- Using classpath relative resources -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- Using mapper interface classes -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- Register all interfaces in a package as mappers -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

感觉方式三用的应该比较多。

Mapper XML 文件

MyBatis 的真正强大在于它的映射语句

SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):

1.cache – 给定命名空间的缓存配置。
2.cache-ref – 其他命名空间缓存配置的引用。
3.resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
4.sql – 可被其他语句引用的可重用语句块。
5.insert – 映射插入语句
6.update – 映射更新语句
7.delete – 映射删除语句
8.select – 映射查询语句

1.select

查询操作相对是比较频繁的。对每个插入、更新或删除操作,通常对应多个查询操作。这是 MyBatis 的基本原则之一,也是将焦点和努力放到1.查询2.结果映射的原因。简单查询的 select 元素是非常简单的。比如:

<select id="selectPerson" parameterType="int" resultType="hashmap">
  SELECT * FROM PERSON WHERE ID = #{id}
</select>

select 元素有很多属性允许你配置,来决定每条语句的作用细节。

<select
  id="selectPerson"
  parameterType="int"
  parameterMap="deprecated"
  resultType="hashmap"
  resultMap="personResultMap"
  flushCache="false"
  useCache="true"
  timeout="10000"
  fetchSize="256"
  statementType="PREPARED"
  resultSetType="FORWARD_ONLY">

关于这些属性的配置意义,参考Mapper XML 文件

几个重要参数:

id–在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType–将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
resultType–从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。

2.insert, update 和 delete

跟insert语句差不多,有些属性增删,需要时查即可。

一个完整的示例:

<insert id="insertAuthor">
  insert into Author (id,username,password,email,bio)
  values (#{id},#{username},#{password},#{email},#{bio})
</insert>

<update id="updateAuthor">
  update Author set
    username = #{username},
    password = #{password},
    email = #{email},
    bio = #{bio}
  where id = #{id}
</update>

<delete id="deleteAuthor">
  delete from Author where id = #{id}
</delete>

重点来看insert操作

对于插入语句的配置规则更加丰富,在插入语句里面有一些额外的属性和子元素用来处理1.主键的生成,而且有多种生成方式。

如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上就OK了。例如,如果上面的 Author 表已经对 id 使用了自动生成的列类型,那么语句可以修改为:

<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username,password,email,bio)
  values (#{username},#{password},#{email},#{bio})
</insert>

这样就省掉了传值的时候还要传递增的id值(有时候这个值是比较麻烦的)。

如果你的数据库还支持2.多行插入, 你也可以传入一个Authors数组或集合,并返回自动生成的主键。

<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username, password, email, bio) values
  <foreach item="item" collection="list" separator=",">
    (#{item.username}, #{item.password}, #{item.email}, #{item.bio})
  </foreach>
</insert>

3.sql

这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。它可以被静态地(在加载参数) 参数化. 不同的属性值通过包含的实例变化. 比如:

<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>

这个可重用的语句可以被这样引用:

<select id="selectUsers" resultType="map">
  select
    <include refid="userColumns"><property name="alias" value="t1"/></include>,
    <include refid="userColumns"><property name="alias" value="t2"/></include>
  from some_table t1
    cross join some_table t2
</select>

通过refid来引用,通过name,value来对可重用的sql语句赋值。

属性值可以用于包含的refid属性或者包含的字句里面的属性值,例如:

<sql id="sometable">
  ${prefix}Table
</sql>

<sql id="someinclude">
  from
    <include refid="${include_target}"/>
</sql>

<select id="select" resultType="map">
  select
    field1, field2, field3
  <include refid="someinclude">
    <property name="prefix" value="Some"/>
    <property name="include_target" value="sometable"/>
  </include>
</select>

该示例中最上层select向下两层进行了赋值。

4.参数(Parameters)

5.Result Maps

resultMap 元素是 MyBatis 中最重要最强大的元素

在前面的示例中,一些简单映射语句的示例,没有明确的 resultMap。比如:

<select id="selectUsers" resultType="map">
  select id, username, hashedPassword
  from some_table
  where id = #{id}
</select>

这样一个语句简单作用于所有列被自动映射到 HashMap 的键上,这由 resultType 属性 指定。这在很多情况下是有用的,但是 HashMap 不能很好描述一个领域模型。那样你的应 用程序将会使用 1.JavaBeans2.POJOs(Plain Old Java Objects,普通 Java 对象)来作为领域 模型。MyBatis 对两者都支持。看看下面这个 JavaBean:

package com.someapp.model;
public class User {
  private int id;
  private String username;
  private String hashedPassword;

  public int getId() {
    return id;
  }
  public void setId(int id) {
    this.id = id;
  }
  public String getUsername() {
    return username;
  }
  public void setUsername(String username) {
    this.username = username;
  }
  public String getHashedPassword() {
    return hashedPassword;
  }
  public void setHashedPassword(String hashedPassword) {
    this.hashedPassword = hashedPassword;
  }
}

基于 JavaBean 的规范,上面这个类有 3 个属性:id,username 和 hashedPassword。这些 在 select 语句中会精确匹配到列名。

这样的一个 JavaBean 可以被映射到结果集,就像映射到 HashMap 一样简单。

<!-- In mybatis-config.xml file -->
<typeAlias type="com.someapp.model.User" alias="User"/>

<!-- In SQL Mapping XML file -->
<select id="selectUsers" resultType="User">
  select id, username, hashedPassword
  from some_table
  where id = #{id}
</select>

如果列名没有精确匹配,你可以在列名上使用 select 字句的别名(一个 基本的 SQL 特性)来匹配标签。比如:

<select id="selectUsers" resultType="User">
  select
    user_id             as "id",
    user_name           as "userName",
    hashed_password     as "hashedPassword"
  from some_table
  where id = #{id}
</select>

以上都是简单的映射。下面来看看高级结果映射

高级结果映射还是比较有难度的,需要时再看看。

6.自动映射

正如你在前面一节看到的,在简单的场景下,MyBatis可以替你自动映射查询结果。 如果遇到复杂的场景,你需要构建一个result map。 但是在本节你将看到,你也可以混合使用这两种策略。 让我们到深一点的层面上看看自动映射是怎样工作的。

当自动映射查询结果时,MyBatis会获取sql返回的列名并在java类中查找相同名字的属性(忽略大小写)。 这意味着如果Mybatis发现了ID列和id属性,Mybatis会将ID的值赋给id

通常数据库列使用大写单词命名,单词间用下划线分隔;而java属性一般遵循驼峰命名法。 为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase设置为true

自动映射甚至在特定的result map下也能工作。在这种情况下,对于每一个result map,所有的ResultSet提供的列, 如果没有被手工映射,则将被自动映射。自动映射处理完毕后手工映射才会被处理。 在接下来的例子中, id 和 userName列将被自动映射, hashed_password 列将根据配置映射。

<select id="selectUsers" resultMap="userResultMap">
  select
    user_id             as "id",
    user_name           as "userName",
    hashed_password
  from some_table
  where id = #{id}
</select>
<resultMap id="userResultMap" type="User">
  <result property="password" column="hashed_password"/>
</resultMap>

有三种自动映射等级:

1.NONE - 禁用自动映射。仅设置手动映射属性。
2.PARTIAL - 将自动映射结果除了那些有内部定义内嵌结果映射的(joins).
3.FULL - 自动映射所有。

默认值是PARTIAL

7.缓存

动态 SQL

1.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”和“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>

2.choose, when, otherwise

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

<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>

3.trim, where, set

现在考虑回到“if”示例,这次我们将“ACTIVE = 1”也设置成动态的条件,看看会发生什么。

<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

同样仅匹配了第二条,会变成:

SELECT * FROM BLOG
WHERE 
AND title like ‘someTitle’

上述两种情况的查询都会失败。那么怎么办呢?上述失败可以使用where元素来避免。

where 元素知道只有在一个以上的if条件有值的情况下才去插入“WHERE”子句。而且,若最后的内容是“AND”或“OR”开头的,where 元素也知道如何将他们去除。改写成where元素形式(并不显示地前置where字符串)

<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>
  </where>
</select>

注意:在用<where>时,千万要加SELECT * FROM BLOG啊!我就在这个小问题上犯错了!还要加上AND或者OR啊!

如果 where 元素没有按正常套路出牌,我们还是可以通过自定义 trim 元素来定制我们想要的功能。比如,和 where 元素等价的自定义 trim 元素为

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

prefixOverrides 属性会忽略通过管道分隔的文本序列(注意此例中的空格也是必要的)。

类似的用于动态更新语句的解决方案叫做 set。set 元素可以被用于动态包含需要更新的列,而舍去其他的。比如:

<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 元素会动态前置 SET 关键字,同时也会消除无关的逗号,因为用了条件语句之后很可能就会在生成的赋值语句的后面留下这些逗号。

若你对等价的自定义 trim 元素的样子感兴趣,那这就应该是它的真面目:

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

4.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作为集合参数当使用可迭代对象或者数组时,index是当前迭代的次数,item的值是本次迭代获取的元素当使用字典(或者Map.Entry对象的集合)时,index是键,item是值

5.bind

bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文。比如:

<select id="selectBlogsLike" resultType="Blog">
  <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
  SELECT * FROM BLOG
  WHERE title LIKE #{pattern}
</select>

Java API

既然你已经知道1.如何配置 MyBatis2.创建映射文件,你就已经准备好来提升技能了。 MyBatis 的 Java API 就是你收获你所做的努力的地方。哈哈哈,出结果的地方终于要来了。

推荐使用的目录结构

1.SqlSessions

在前面的内容中,我们学习到,创建SqlSessions的一般步骤是:SqlSessionFactoryBuilder->SqlSessionFactory->SqlSession。

简化操作
当Mybatis与一些依赖注入框架(如Spring或者Guice)同时使用时,SqlSessions将被依赖注入框架所创建,所以你不需要使用SqlSessionFactoryBuilder或者SqlSessionFactory,可以直接看SqlSession这一节。请参考Mybatis-Spring或者Mybatis-Guice手册了解更多信息。

SqlSession

直接看这里

如上面所提到的,SqlSession 实例在 MyBatis 中是非常强大的一个类。在这里你会发现 所有执行语句的方法,提交或回滚事务,还有获取映射器实例。

在 SqlSession 类中有超过 20 个方法,所以将它们分开成易于理解的组合。

1.语句执行方法

这些方法被用来执行定义在 SQL 映射的 XML 文件中的 SELECT,INSERT,UPDA E T 和 DELETE 语句。它们都会自行解释,每一句都使用语句的 ID 属性参数对象,参数可以 是原生类型(自动装箱或包装类) ,JavaBean,POJO 或 Map。

<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)

selectOne 和 selectList 的不同仅仅是 selectOne 必须返回一个对象。 如果多余一个, 或者 没有返回 (或返回了 null) 那么就会抛出异常。 , 如果你不知道需要多少对象, 使用 selectList

如果你想检查一个对象是否存在,那么最好返回统计数(0 或 1) 。因为并不是所有语句都需 要参数,这些方法都是有不同重载版本的,它们可以不需要参数对象。

<T> T selectOne(String statement)
<E> List<E> selectList(String statement)
<K,V> Map<K,V> selectMap(String statement, String mapKey)
int insert(String statement)
int update(String statement)
int delete(String statement)

最后,还有查询方法的三个高级版本,它们允许你限制返回行数的范围,或者提供自定 义结果控制逻辑,这通常用于大量的数据集合。

<E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds)
void select (String statement, Object parameter, ResultHandler<T> handler)
void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler<T> handler)

RowBounds 参数会告诉 MyBatis 略过指定数量的记录,还有限制返回结果的数量。 RowBounds 类有一个构造方法来接收 offset 和 limit,否则是不可改变的。 这个功能在网站返回分页结果的地方正好有用!

int offset = 100;
int limit = 25;
RowBounds rowBounds = new RowBounds(offset, limit);

不同的驱动会实现这方面的不同级别的效率。对于最佳的表现,使用结果集类型的 SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE(换句话说:不是 FORWARD_ONLY)

ResultHandler 参数允许你按你喜欢的方式处理每一行你可以将它添加到 List 中,创 建 Map, 或抛出每个结果而不是只保留总计。 Set 你可以使用 ResultHandler 做很多漂亮的事, 那就是 MyBatis 内部创建结果集列表。

它的接口很简单。

package org.apache.ibatis.session;
public interface ResultHandler<T> {
  void handleResult(ResultContext<? extends T> context);
}

ResultContext 参数给你访问结果对象本身的方法, 大量结果对象被创建, 你可以使用布尔返回值的 stop()方法来停止 MyBatis 加载更多的结果。

实现这个接口看来很有用且很方便。

2.批量立即更新方法(Flush Method)

有一个方法可以刷新(执行)存储在JDBC驱动类中的批量更新语句。当你将ExecutorType.BATCH作为ExecutorType使用时可以采用此方法。

List<BatchResult> flushStatements()

3.事务控制方法

控制事务作用域有四个方法。 当然, 如果你已经选择了自动提交或你正在使用外部事务管理器,这就没有任何效果了。也就是说,前提是没有使用自动提交或者外部事务处理器。如果你正在使用 JDBC 事务管理员,由 Connection 实 例来控制,那么这四个方法就会派上用场:

void commit()
void commit(boolean force)
void rollback()
void rollback(boolean force)

MyBatis-Spring和MyBatis-Guice提供了声明事务处理,所以如果你在使用Mybatis的同时使用了Spring或者Guice,那么请参考它们的手册以获取更多的内容。

4.清理 Session 级的缓存

void clearCache()

SqlSession 实例有一个本地缓存在执行 update,commit,rollback 和 close 时被清理。要 明确地关闭它(获取打算做更多的工作) ,你可以调用 clearCache()。

5.确保 SqlSession 被关闭

void close()

你必须保证的最重要的事情是你要关闭所打开的任何 session。保证做到这点的最佳方 式是下面的工作模式:

SqlSession session = sqlSessionFactory.openSession();
try {
    // following 3 lines pseudocod for "doing some work"
    session.insert(...);
    session.update(...);
    session.delete(...);
    session.commit();
} finally {
    session.close();
}

可以通过调用 getConfiguration()方法获得 SqlSession 使用的 Configuration 实例:

Configuration getConfiguration()

以上这些方法的调用主体都是SqlSession哦,不要忘记了。

6.使用映射器

<T> T getMapper(Class<T> type)

上述的各个 insert,update,delete 和 select 方法都很强大,但也有些繁琐,没有类型安全。

因此, 一个更通用的方式来执行映射语句是使用映射器类。 一个映射器类就是一个简单 的接口,其中的方法定义匹配于 SqlSession 方法。下面的示例展示了一些方法签名和它们是 如何映射到 SqlSession 的。

public interface AuthorMapper {
  // (Author) selectOne("selectAuthor",5);
  Author selectAuthor(int id); 
  // (List<Author>) selectList(“selectAuthors”)
  List<Author> selectAuthors();
  // (Map<Integer,Author>) selectMap("selectAuthors", "id")
  @MapKey("id")
  Map<Integer, Author> selectAuthors();
  // insert("insertAuthor", author)
  int insertAuthor(Author author);
  // updateAuthor("updateAuthor", author)
  int updateAuthor(Author author);
  // delete("deleteAuthor",5)
  int deleteAuthor(int id);
}

总之, 每个映射器方法签名应该匹配相关联的 SqlSession 方法, 而没有字符串参数 ID。 相反,方法名必须匹配映射语句的 ID。 这一点非常重要啊!不然,光有调用方式和接口定义,怎么框架怎么知道具体的sql语句呢?

映射器接口不需要去实现任何接口或扩展任何类。 只要方法前面可以被用来唯一标识对 应的映射语句就可以了。

7.映射器注解

因为最初设计时,MyBatis 是一个 XML 驱动的框架。配置信息是基于 XML 的,而且映射语句也是定义在 XML 中的。而到了 MyBatis 3,有新的可用的选择了。MyBatis 3 构建 在基于全面而且强大的 Java 配置 API 之上。这个配置 API 是基于 XML 的 MyBatis 配置的 基础,也是新的基于注解配置的基础。注解提供了一种简单的方式来实现简单映射语句,而 不会引入大量的开销。

不幸的是,Java 注解限制了它们的表现和灵活。所以,要实现复杂的功能,还是使用xml进行配置吧。

请参考具体的注解

SQL语句构建器类

1.问题

Java程序员面对的最痛苦的事情之一就是在Java代码中嵌入SQL语句。这么来做通常是由于SQL语句需要动态来生成-否则可以将它们放到外部文件或者存储过程中。正如你已经看到的那样,MyBatis在它的XML映射特性中有一个强大的动态SQL生成方案。但有时在Java代码内部创建SQL语句也是必要的。此时,MyBatis有另外一个特性可以帮到你,在减少典型的加号,引号,新行,格式化问题和嵌入条件来处理多余的逗号或 AND 连接词之前。事实上,在Java代码中来动态生成SQL代码就是一场噩梦。例如:

String sql = "SELECT P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME, "
"P.LAST_NAME,P.CREATED_ON, P.UPDATED_ON " +
"FROM PERSON P, ACCOUNT A " +
"INNER JOIN DEPARTMENT D on D.ID = P.DEPARTMENT_ID " +
"INNER JOIN COMPANY C on D.COMPANY_ID = C.ID " +
"WHERE (P.ID = A.ID AND P.FIRST_NAME like ?) " +
"OR (P.LAST_NAME like ?) " +
"GROUP BY P.ID " +
"HAVING (P.LAST_NAME like ?) " +
"OR (P.FIRST_NAME like ?) " +
"ORDER BY P.ID, P.FULL_NAME";

2.The Solution

使用SQL类,简单地创建一个实例来调用方法生成SQL语句。上面示例中的问题就像重写SQL类那样:

private String selectPersonSql() {
  return new SQL() {{
    SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME");
    SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON");
    FROM("PERSON P");
    FROM("ACCOUNT A");
    INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID");
    INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID");
    WHERE("P.ID = A.ID");
    WHERE("P.FIRST_NAME like ?");
    OR();
    WHERE("P.LAST_NAME like ?");
    GROUP_BY("P.ID");
    HAVING("P.LAST_NAME like ?");
    OR();
    HAVING("P.FIRST_NAME like ?");
    ORDER_BY("P.ID");
    ORDER_BY("P.FULL_NAME");
  }}.toString();
}

3.SQL类

具体的方法,像SELECT,INNER_JOIN等请参考方法

更多

一、关于iBatis与JDBC的比较:Eliminates a lot of JDBC boilerplate code

Java has a Java DataBase Connectivity (JDBC) API to work with relational databases. But JDBC is a very low-level API, and we need to write a lot of code to perform database operations.

该文章说明了使用步骤:

1.Configure the queries in a SQL Mapper config file, say StudentMapper.xml.

<select id="findStudentById" parameterType="int"  
resultType=" Student">
  SELECT STUD_ID AS studId, NAME, EMAIL, DOB 
  FROM STUDENTS WHERE STUD_ID=#{Id}
</select>

2.Create a StudentMapper interface.

public interface StudentMapper
{
  Student findStudentById(Integer id);
}

3.In Java code, you can invoke these statements as follows:

SqlSession session = getSqlSessionFactory().openSession();
StudentMapper mapper =  
session.getMapper(StudentMapper.class);
// Select Student by Id
Student student = mapper.selectStudentById(1);

二、关于springBoot与iBatis的综合:
mybatis-spring-boot-autoconfigure

该docs坑了我一把啊,两个例子并不能运行。究其原因是我mysql数据库没有配置正确,参考Spring Boot Reference Guide配置文档,寻找spring.datasource开头的字眼,这是springBoot连接数据库需要做的一些配置,最后application.properties配置如下:

#执行语句
spring.datasource.schema=classpath:import.sql
#指明数据库
spring.datasource.url = jdbc:mysql://localhost:3306/test
#用户名
spring.datasource.username = ×××
#密码
spring.datasource.password = ×××
#固定,同时需要下载jdbc驱动
spring.datasource.driver-class-name= com.mysql.jdbc.Driver

对于最后一条,还需要在maven中下载驱动,添加依赖:

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>

import.sql中内容:

create table city(id int primary key auto_increment, name VARCHAR(40), state VARCHAR(40), country VARCHAR(40));

insert into city (name, state, country) values ('San Francisco', 'CA', 'US');

成功运行。

注意:在类和xml返回结果映射时,是忽略大小写的。也就是说,如果类中有属性ID,数据库返回的字段为id,那么是可以映射成功的。但是数据库下划线和java驼峰命令之间不能自动互相转换,应该加入下列配置:

<settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

在config的时候还需要注意的是,<configuration>下元素是需要有顺序的,如下:

<?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>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <mappers>
        <mapper resource="mappers/GoodsMapper.xml"/>
    </mappers>
</configuration>

如果将<mappers>提前到<settings>前面,那么该配置会报错。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值