总结:
1、
mybaits配置工2方面:
i行为配置,如数据源的实现是否利用池pool的概念(POOLED – This implementation of DataSource pools JDBC Connection objects to avoid the initial connection and authentication time required to create a new Connection instance.This is a popular approach for concurrent web applications to achieve the fastest response.);
iisql映射非配置;
2、
1级别、2级别本地缓存;
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。
3、
驱动程序等待数据库返回请求结果的秒数
默认依赖驱动
4、
sql重用
5、
ResultMap 的设计思想是
简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。
The design of the ResultMaps is such that simple statements don't require explicit result mappings at all, and more complex statements require no more than is absolutely necessary to describe the relationships.
6、
resultType resultMap区别
对数据库返回数据的映射的方式
两者须且只须用1,前者对于单表查询或者属性简单的对象相对实用;
简单的对象,resultType="map"即可
典型例子
<select id="selectUsers" resultType="map">
select id, username, phone
from users
where id = #{id}
</select>
7、
使用单元测试建立结果映射;
8、
缓存算法
可用的收回策略有:
- LRU – 最近最少使用的:移除最长时间不被使用的对象。
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
- WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
9、
mybaits跨数据库、 limit 传参与spring boot rest api 整合
@1
Mapper XML 文件
MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 就是针对 SQL 构建的,并且比普通的方法做的更好。
SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):
- cache – 给定命名空间的缓存配置。
- cache-ref – 其他命名空间缓存配置的引用。
- resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
parameterMap– 已废弃!老式风格的参数映射。内联参数是首选,这个元素可能在将来被移除,这里不会记录。- sql – 可被其他语句引用的可重用语句块。
- insert – 映射插入语句
- update – 映射更新语句
- delete – 映射删除语句
- select – 映射查询语句
Mapper XML Files
The true power of MyBatis is in the Mapped Statements. This is where the magic happens. For all of their power, the Mapper XML files are relatively simple. Certainly if you were to compare them to the equivalent JDBC code, you would immediately see a savings of 95% of the code. MyBatis was built to focus on the SQL, and does its best to stay out of your way.
The Mapper XML files have only a few first class elements (in the order that they should be defined):
- cache – Configuration of the cache for a given namespace.
- cache-ref – Reference to a cache configuration from another namespace.
- resultMap – The most complicated and powerful element that describes how to load your objects from the database result sets.
parameterMap– Deprecated! Old-school way to map parameters. Inline parameters are preferred and this element may be removed in the future. Not documented here.- sql – A reusable chunk of SQL that can be referenced by other statements.
- insert – A mapped INSERT statement.
- update – A mapped UPDATE statement.
- delete – A mapped DELETE statement.
- select – A mapped SELECT statement.
The next sections will describe each of these elements in detail, starting with the statements themselves.
@2
flushCache 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。
useCache 将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。
flushCache Setting this to true will cause the local and 2nd level caches to be flushed whenever this statement is called. Default: false for select statements.
useCache Setting this to true will cause the results of this statement to be cached in 2nd level cache. Default: true for select statements.
@3
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
timeout This sets the number of seconds the driver will wait for the database to return from a request, before throwing an exception. Default is unset (driver dependent).
@4
sql
这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。它可以被静态地(在加载参数) 参数化. 不同的属性值通过包含的实例变化. 比如:
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
这个 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>
属性值也可以被用在 include 元素的 refid 属性里(
<include refid="${include_target}"/>
${prefix}Table
<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>
@6
resultType 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。
resultMap 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用。
resultType The fully qualified class name or alias for the expected type that will be returned from this statement. Note that in the case of collections, this should be the type that the collection contains, not the type of the collection itself. Use resultType OR resultMap, not both.
resultMap A named reference to an external resultMap. Result maps are the most powerful feature of MyBatis, and with a good understanding of them, many difficult mapping cases can be solved. Use resultMap OR resultType, not both.
Result Maps
resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来, 并在一些情形下允许你做一些 JDBC 不支持的事情。 实际上,在对复杂语句进行联合映射的时候,它很可能可以代替数千行的同等功能的代码。 ResultMap 的设计思想是,简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。
你已经见过简单映射语句的示例了,但没有明确的 resultMap。比如:
<select id="selectUsers" resultType="map">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>
上述语句只是简单地将所有的列映射到 HashMap 的键上,这由 resultType 属性指定。 虽然在大部分情况下都够用,但是 HashMap 不是一个很好的领域模型。 你的程序更可能会使用 JavaBean 或 POJO(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 可以被映射到 ResultSet,就像映射到 HashMap 一样简单。
<select id="selectUsers" resultType="com.someapp.model.User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>
类型别名是你的好帮手。使用它们,你就可以不用输入类的完全限定名称了。比如:
<!-- 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>
这些情况下,MyBatis 会在幕后自动创建一个 ResultMap,再基于属性名来映射列到 JavaBean 的属性上。如果列名和属性名没有精确匹配,可以在 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>
ResultMap 最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。 上面这些简单的示例根本不需要下面这些繁琐的配置。 出于示范的原因,让我们来看看最后一个示例中,如果使用外部的 resultMap 会怎样,这也是解决列名不匹配的另外一种方式。
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>
引用它的语句使用 resultMap 属性就行了(注意我们去掉了 resultType 属性)。比如:
<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>
如果世界总是这么简单就好了。
高级结果映射
MyBatis 创建的一个想法是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们不总都是这样。 如果有一个独立且完美的数据库映射模式,所有应用程序都可以使用它,那就太好了,但可惜也没有。 ResultMap 就是 MyBatis 对这个问题的答案。
比如,我们如何映射下面这个语句?
<!-- Very Complex Statement -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio,
A.favourite_section as author_favourite_section,
P.id as post_id,
P.blog_id as post_blog_id,
P.author_id as post_author_id,
P.created_on as post_created_on,
P.section as post_section,
P.subject as post_subject,
P.draft as draft,
P.body as post_body,
C.id as comment_id,
C.post_id as comment_post_id,
C.name as comment_name,
C.comment as comment_text,
T.id as tag_id,
T.name as tag_name
from Blog B
left outer join Author A on B.author_id = A.id
left outer join Post P on B.id = P.blog_id
left outer join Comment C on P.id = C.post_id
left outer join Post_Tag PT on PT.post_id = P.id
left outer join Tag T on PT.tag_id = T.id
where B.id = #{id}
</select>
你可能想把它映射到一个智能的对象模型,这个对象表示了一篇博客,它由某位作者所写, 有很多的博文,每篇博文有零或多条的评论和标签。 我们来看看下面这个完整的例子,它是一个非常复杂的 ResultMap (假设作者,博客,博文,评论和标签都是类型的别名)。 不用紧张,我们会一步一步来说明。 虽然它看起来令人望而生畏,但其实非常简单。
<!-- 超复杂的 Result Map -->
<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<result property="title" column="blog_title"/>
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
<result property="favouriteSection" column="author_favourite_section"/>
</association>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<association property="author" javaType="Author"/>
<collection property="comments" ofType="Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType="Tag" >
<id property="id" column="tag_id"/>
</collection>
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>
Result Maps
The resultMap element is the most important and powerful element in MyBatis. It's what allows you to do away with 90% of the code that JDBC requires to retrieve data from ResultSets, and in some cases allows you to do things that JDBC does not even support. In fact, to write the equivalent code for something like a join mapping for a complex statement could probably span thousands of lines of code. The design of the ResultMaps is such that simple statements don't require explicit result mappings at all, and more complex statements require no more than is absolutely necessary to describe the relationships.
You've already seen examples of simple mapped statements that don't have an explicit resultMap. For example:
<select id="selectUsers" resultType="map">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>
Such a statement simply results in all columns being automatically mapped to the keys of a HashMap, as specified by the resultType attribute. While useful in many cases, a HashMap doesn't make a very good domain model. It's more likely that your application will use JavaBeans or POJOs (Plain Old Java Objects) for the domain model. MyBatis supports both. Consider the following 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;
}
}
Based on the JavaBeans specification, the above class has 3 properties: id, username, and hashedPassword. These match up exactly with the column names in the select statement.
Such a JavaBean could be mapped to a ResultSet just as easily as the HashMap.
<select id="selectUsers" resultType="com.someapp.model.User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>
And remember that TypeAliases are your friends. Use them so that you don't have to keep typing the fully qualified path of your class out. For example:
<!-- In 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>
In these cases MyBatis is automatically creating a ResultMap behind the scenes to auto-map the columns to the JavaBean properties based on name. If the column names did not match exactly, you could employ select clause aliases (a standard SQL feature) on the column names to make the labels match. For example:
<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>
The great thing about ResultMaps is that you've already learned a lot about them, but you haven't even seen one yet! These simple cases don't require any more than you've seen here. Just for example sake, let's see what this last example would look like as an external resultMap, as that is another way to solve column name mismatches.
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>
And the statement that references it uses the resultMap attribute to do so (notice we removed the resultType attribute). For example:
<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>
Now if only the world was always that simple.
Advanced Result Maps
MyBatis was created with one idea in mind: Databases aren't always what you want or need them to be. While we'd love every database to be perfect 3rd normal form or BCNF, they aren't. And it would be great if it was possible to have a single database map perfectly to all of the applications that use it, it's not. Result Maps are the answer that MyBatis provides to this problem.
For example, how would we map this statement?
<!-- Very Complex Statement -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio,
A.favourite_section as author_favourite_section,
P.id as post_id,
P.blog_id as post_blog_id,
P.author_id as post_author_id,
P.created_on as post_created_on,
P.section as post_section,
P.subject as post_subject,
P.draft as draft,
P.body as post_body,
C.id as comment_id,
C.post_id as comment_post_id,
C.name as comment_name,
C.comment as comment_text,
T.id as tag_id,
T.name as tag_name
from Blog B
left outer join Author A on B.author_id = A.id
left outer join Post P on B.id = P.blog_id
left outer join Comment C on P.id = C.post_id
left outer join Post_Tag PT on PT.post_id = P.id
left outer join Tag T on PT.tag_id = T.id
where B.id = #{id}
</select>
You'd probably want to map it to an intelligent object model consisting of a Blog that was written by an Author, and has many Posts, each of which may have zero or many Comments and Tags. The following is a complete example of a complex ResultMap (assume Author, Blog, Post, Comments and Tags are all type aliases). Have a look at it, but don't worry, we're going to go through each step. While it may look daunting at first, it's actually very simple.
<!-- Very Complex Result Map -->
<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<result property="title" column="blog_title"/>
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
<result property="favouriteSection" column="author_favourite_section"/>
</association>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<association property="author" javaType="Author"/>
<collection property="comments" ofType="Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType="Tag" >
<id property="id" column="tag_id"/>
</collection>
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>
mybatis – MyBatis 3 | Mapper XML 文件 http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html
@8
缓存
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。MyBatis 3 中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。
默认情况下是没有开启缓存的,除了局部的 session 缓存,可以增强变现而且处理循环 依赖也是必须的。要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:
<cache/>
字面上看就是这样。这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句将会被缓存。
- 映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
- 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
- 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
- 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
- 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
NOTE The cache will only apply to statements declared in the mapping file where the cache tag is located. If you are using the Java API in conjunction with the XML mapping files, then statements declared in the companion interface will not be cached by default. You will need to refer to the cache region using the @CacheNamespaceRef annotation.
所有的这些属性都可以通过缓存元素的属性来修改。比如:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。
可用的收回策略有:
- LRU – 最近最少使用的:移除最长时间不被使用的对象。
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
- WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU。
flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒 形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓 存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。
使用自定义缓存
除了这些自定义缓存的方式, 你也可以通过实现你自己的缓存或为其他第三方缓存方案 创建适配器来完全覆盖缓存行为。
<cache type="com.domain.something.MyCustomCache"/>
这个示 例展 示了 如何 使用 一个 自定义 的缓 存实 现。type 属 性指 定的 类必 须实现 org.mybatis.cache.Cache 接口。这个接口是 MyBatis 框架中很多复杂的接口之一,但是简单 给定它做什么就行。
public interface Cache {
String getId();
int getSize();
void putObject(Object key, Object value);
Object getObject(Object key);
boolean hasKey(Object key);
Object removeObject(Object key);
void clear();
}
要配置你的缓存, 简单和公有的 JavaBeans 属性来配置你的缓存实现, 而且是通过 cache 元素来传递属性, 比如, 下面代码会在你的缓存实现中调用一个称为 “setCacheFile(String file)” 的方法:
<cache type="com.domain.something.MyCustomCache">
<property name="cacheFile" value="/tmp/my-custom-cache.tmp"/>
</cache>
你可以使用所有简单类型作为 JavaBeans 的属性,MyBatis 会进行转换。 And you can specify a placeholder(e.g. ${cache.file}) to replace value defined at configuration properties.
从3.4.2版本开始,MyBatis已经支持在所有属性设置完毕以后可以调用一个初始化方法。如果你想要使用这个特性,请在你的自定义缓存类里实现 org.apache.ibatis.builder.InitializingObject 接口。
public interface InitializingObject {
void initialize() throws Exception;
}
记得缓存配置和缓存实例是绑定在 SQL 映射文件的命名空间是很重要的。因此,所有 在相同命名空间的语句正如绑定的缓存一样。 语句可以修改和缓存交互的方式, 或在语句的 语句的基础上使用两种简单的属性来完全排除它们。默认情况下,语句可以这样来配置:
<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>
因为那些是默认的,你明显不能明确地以这种方式来配置一条语句。相反,如果你想改 变默认的行为,只能设置 flushCache 和 useCache 属性。比如,在一些情况下你也许想排除 从缓存中查询特定语句结果,或者你也许想要一个查询语句来刷新缓存。相似地,你也许有 一些更新语句依靠执行而不需要刷新缓存。
参照缓存
回想一下上一节内容, 这个特殊命名空间的唯一缓存会被使用或者刷新相同命名空间内 的语句。也许将来的某个时候,你会想在命名空间中共享相同的缓存配置和实例。在这样的 情况下你可以使用 cache-ref 元素来引用另外一个缓存。
<cache-ref namespace="com.someone.application.data.SomeMapper"/>
<mappers>
<mapper resource="./mappers/StudentMapper.xml"/>
<mapper resource="./mappers/PageVisitorMapper.xml"/>
</mappers>
### Error building SqlSession. ### The error may exist in ./mappers/StudentMapper.xml ### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource ./mappers/StudentMapper.xml
@@
http://onf:8081/testMyBaitsInsert
当库已经被删除
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Thu Dec 27 18:42:19 CST 2018
There was an unexpected error (type=Internal Server Error, status=500).
### Error updating database. Cause: java.sql.SQLException: Unknown database 'video_test' ### Cause: java.sql.SQLException: Unknown database 'video_test'
当主键id没有设置自增
### Error updating database. Cause: java.sql.SQLException: Field 'id' doesn't have a default value ### The error may involve Student.insert-Inline ### The error occurred while setting parameters ### SQL: INSERT INTO STUDENT (NAME, BRANCH, PERCENTAGE, PHONE, EMAIL ) VALUES (?, ?, ?, ?, ?); ### Cause: java.sql.SQLException: Field 'id' doesn't have a default value
跨数据库
http://onf:8081/getVisitedPage/34000256/3/20
{"debug":"com.test.controller.HelloControllergetVisitedPage","List<PageVisitor> pageVisitors":"http://www.ijntv.cn/inews/56456305.htmlhttp://www.ijntv.cn/inews/bUPp64oTzNWIBYBSBX9H8g.htmlhttp://www.ijntv.cn/inews/RAhXw3bFrAdjP5bfx9NYvg.html","totalPv":"123"}
回数据库校验
SELECT * FROM page_last_visitor WHERE uid=34000256 ORDER BY modify_time DESC LIMIT 3 OFFSET 20;
[root@f ~]# tree /mnt/gateway/javatest/
/mnt/gateway/javatest/
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── test
│ │ │ ├── controller
│ │ │ │ └── HelloController.java
│ │ │ ├── dataAccessObj
│ │ │ │ ├── PageVisitor.java
│ │ │ │ ├── Student.java
│ │ │ │ └── TitleKeyword.java
│ │ │ ├── EsJava
│ │ │ │ └── RestClientTest.java
│ │ │ ├── HelloApplication.java
│ │ │ ├── learnJava
│ │ │ │ └── javaStep.java
│ │ │ ├── long2ip
│ │ │ │ ├── Long2ipConversion.java
│ │ │ │ └── README.md
│ │ │ ├── MybaitsMapper
│ │ │ ├── publicComponent
│ │ │ │ └── asymmetricCryptographicAlgorithm
│ │ │ │ └── EnAndDe.java
│ │ │ ├── RSA
│ │ │ │ └── JavaRsa.java
│ │ │ └── sqlBuilder
│ │ │ ├── GenStr.java
│ │ │ └── Student.java
│ │ └── resources
│ │ ├── application.properties
│ │ ├── PageVisitorMapper.xml
│ │ ├── SqlMapConfig.xml
│ │ └── StudentMapper.xml
│ └── test
│ └── java
└── target
├── classes
│ ├── application.properties
│ ├── com
│ │ └── test
│ │ ├── controller
│ │ │ ├── HelloController$1.class
│ │ │ ├── HelloController$2.class
│ │ │ └── HelloController.class
│ │ ├── dataAccessObj
│ │ │ ├── PageVisitor.class
│ │ │ ├── Student.class
│ │ │ └── TitleKeyword.class
│ │ ├── EsJava
│ │ │ ├── RestClientTest$1.class
│ │ │ └── RestClientTest.class
│ │ ├── HelloApplication.class
│ │ ├── learnJava
│ │ │ └── javaStep.class
│ │ ├── long2ip
│ │ │ └── Long2ipConversion.class
│ │ ├── publicComponent
│ │ │ └── asymmetricCryptographicAlgorithm
│ │ │ └── EnAndDe.class
│ │ ├── RSA
│ │ │ └── JavaRsa.class
│ │ └── sqlBuilder
│ │ ├── GenStr$1.class
│ │ ├── GenStr.class
│ │ ├── Student$1.class
│ │ └── Student.class
│ ├── PageVisitorMapper.xml
│ ├── SqlMapConfig.xml
│ └── StudentMapper.xml
├── maven-archiver
│ └── pom.properties
├── springMybatis4Debug-1.0-SNAPSHOT.jar
├── springMybatis4Debug-1.0-SNAPSHOT.jar.original
└── surefire
33 directories, 42 files
[root@f ~]#
D:workspspringtestsrcmain esourcesSqlMapConfig.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>
<typeAliases>
<typeAlias alias="Student" type="com.test.dataAccessObj.Student"/>
<typeAlias alias="PageVisitor" type="com.test.dataAccessObj.PageVisitor"/>
</typeAliases>
<environments default="development">
<environment id="rdsTest">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://rm-2jepye0j3957bw1w2o.mysql.rds.aliyuncs.com:3306/video_test"/>
<property name="username" value="ut"/>
<property name="password" value="pt"/>
</dataSource>
</environment>
<environment id="ad_detail_page">
<transactionManager type="JDBC"/>
<!--POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这是一种使得并发 Web 应用快速响应请求的流行处理方式。-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://11.21.41.72:3306/ad_detail_page"/>
<property name="username" value="u"/>
<!--TODO-->
<property name="password" value="3gA"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="StudentMapper.xml"/>
<mapper resource="PageVisitorMapper.xml"/>
</mappers>
</configuration>
D:workspspringtestsrcmain esourcesPageVisitorMapper.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="PageVisitor">
<select id="getPageVisitor" resultMap="PageInfo" flushCache="false" useCache="true">
SELECT address,pv,modify_time,ip FROM page_last_visitor WHERE uid=#{uid} ORDER BY modify_time DESC
</select>
<resultMap id="PageInfo" type="PageVisitor">
<!--也可以在sql中队列使用别名,resultType="Map";或者直接修改数据库-->
<result property="url" column="address"/>
<result property="pv" column="pv"/>
<result property="updateTime" column="modify_time"/>
<result property="ip" column="ip"/>
</resultMap>
<!--pv数据部分实时更新,历史数据,缓存算法调整-->
</mapper>
D:workspspringtestsrcmain esourcesStudentMapper.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="Student">
<insert id="insert" parameterType="Student">
INSERT INTO STUDENT (NAME, BRANCH, PERCENTAGE, PHONE, EMAIL ) VALUES (#{name}, #{branch}, #{percentage},
#{phone}, #{email});
<selectKey keyProperty="id" resultType="int" order="AFTER">
select last_insert_id() as id
</selectKey>
</insert>
<select id="getRecByName" parameterType="Student" resultMap="result">
SELECT * FROM STUDENT
<if test="name != null">
WHERE name LIKE %#{name}%
</if>
</select>
<select id="getStudentById" parameterType="Student" resultMap="result">
SELECT * FROM STUDENT WHERE id = #{id}
</select>
<select id="getRecByNameWildcard" parameterType="Student" resultMap="result">
SELECT * FROM STUDENT
<if test="name != null">
WHERE name LIKE "%"#{name}"%"
</if>
</select>
<!--TODO 优化:limit传参-->
<select id="getRecByNameLimit1" parameterType="Student" resultMap="result">
SELECT * FROM STUDENT
<if test="name != null">
WHERE name LIKE #{name}
</if>
LIMIT 1
</select>
<select id="getAll" resultMap="result">
SELECT * FROM STUDENT;
</select>
<resultMap id="result" type="Student">
<result property="id" column="ID"/>
<result property="name" column="NAME"/>
<result property="branch" column="BRANCH"/>
<result property="percentage" column="PERCENTAGE"/>
<result property="phone" column="PHONE"/>
<result property="email" column="EMAIL"/>
</resultMap>
</mapper>
D:workspspringtestsrcmainjavacom estdataAccessObjPageVisitor.java
package com.test.dataAccessObj;
public class PageVisitor {
private String url;
private String pv;
private int updateTime;
private long ip;
public PageVisitor() {
}
public int getUpdateTime() {
return updateTime;
}
public long getIp() {
return ip;
}
public String getPv() {
return pv;
}
public String getUrl() {
return url;
}
}
D:workspspringtestsrcmainjavacom estcontrollerHelloController.java
package com.test.controller;
import com.alibaba.fastjson.JSONObject;
import com.test.dataAccessObj.PageVisitor;
import com.test.dataAccessObj.Student;
import com.test.dataAccessObj.TitleKeyword;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.management.ManagementFactory;
import java.util.Collections;
import java.util.List;
@RestController
public class HelloController {
@RequestMapping("/testMyBaitsInsert")
public String testMyBaits() throws IOException {
String res = this.getClass().getName() + Thread.currentThread().getStackTrace()[1].getMethodName();
Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml");
try {
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader, "rdsTest");
SqlSession session = sqlSessionFactory.openSession();
try {
//Create a new student object
Student student;
for (int i = 0; i < 10; i++) {
student = new Student("Mohammad", "It", 80, i * i, System.currentTimeMillis() + "Mohammad@gmail.com");
//Insert student data
session.insert("Student.insert", student);
System.out.println("record inserted successfully" + Integer.toString(i));
}
session.commit();
} finally {
session.close();
}
} catch (IndexOutOfBoundsException e) {
System.err.println("IndexOutOfBoundsException: " + e.getMessage());
}
return res;
}
@GetMapping("/getSingleStudent/{encryptedId}")
public ResponseEntity getSingleStudent(@PathVariable int encryptedId) throws IOException {
String res = this.getClass().getName() + Thread.currentThread().getStackTrace()[1].getMethodName();
Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader, "rdsTest");
SqlSession session = sqlSessionFactory.openSession();
try {
Student student = (Student) session.selectOne("Student.getStudentById", encryptedId);
session.commit();
res += student.getName() + student.getEmail() + student.getBranch();
} finally {
session.close();
}
JSONObject jsonObject = JSONObject.parseObject("{"totalPv":"123"}");
return new ResponseEntity<JSONObject>(jsonObject, HttpStatus.OK);
}
@GetMapping("/getES653/{requestTitle}")
public ResponseEntity getES653(@PathVariable String requestTitle) throws IOException {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
String TitleIndex = "search_title";
String TextIndex = "search_text";
long timeMillisSpent = 0;
long startEsSearch = System.currentTimeMillis();
long endEsSearch;
String res = "";
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials("elastic", "123"));
RestClient restClient = RestClient.builder(new HttpHost("es-cn-mp90abc000c3ats.elasticsearch.aliyuncs.com", 9200))
.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
@Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
}
}).build();
try {
String searchStrWithoutTitle = "{"from":0,"size":200,"query":{"bool":{"should":[{"match":{"keyword":{"query":"宝宝","boost":2}}},{"match":{"text":{"query":"婴幼儿,教育,家庭教育,幼儿培训,早教培训,早教课程","boost":1}}}]}},"track_scores":"true","sort":[{"weight":{"order":"desc"}},{"_score":{"order":"desc"}}]}";
String searchStrWithTitle = "{"from":0,"size":200,"query":{"bool":{"should":{"match":{"title":{"query":" + requestTitle + ","boost":1}}}}},"track_scores":"true","sort":[{"weight":{"order":"desc"}},{"_score":{"order":"desc"}}]}";
HttpEntity httpEntity = new StringEntity(searchStrWithoutTitle, ContentType.APPLICATION_JSON);
//search a document
System.out.println(searchStrWithTitle);
String endpoint = "/search_text/_search?";
Response response = restClient.performRequest("GET", endpoint,
Collections.singletonMap("pretty", "true"), httpEntity);
endEsSearch = System.currentTimeMillis();
timeMillisSpent = endEsSearch - startEsSearch;
res = EntityUtils.toString(response.getEntity());
System.out.println("searchES-TimeMillisSpent:" + timeMillisSpent);
} catch (IOException e) {
e.printStackTrace();
}
String timeMillisSpentStr = Long.toString(timeMillisSpent);
JSONObject jsonObject = JSONObject.parseObject(res);
return new ResponseEntity<JSONObject>(jsonObject, HttpStatus.OK);
}
@RequestMapping(value = "/getESversion553ByPost", method = RequestMethod.POST)
public ResponseEntity getES653(@RequestBody TitleKeyword titleKeyword) throws IOException {
long startMethod = System.currentTimeMillis();
long searchESTimeMillisSpent = 0;
String title = titleKeyword.getTitle();
String keyword = titleKeyword.getKeyword();
String keywords = titleKeyword.getKeywords();
String searchESstr;
String resJsonStr;
JSONObject jsonObject;
HttpStatus httpStatus = HttpStatus.OK;
String kwsSeparatorStr = ",";
// TODO searchESstr公共部分摘除
// 假设优先级 主关键词>关键词列表>标题,有关键词存在情况下,不考虑标题
if (keyword != null && keyword.replace(" ", "").length() > 0) {
if (keywords != null && keywords.replace(" ", "").length() > 0) {
if (keywords.contains(kwsSeparatorStr)) {
String[] requestKeywordList = keywords.split(kwsSeparatorStr);
// TODO 此处需要结合es数据结构请求的数据结构进行算法优化,使得es搜索结果精准
keywords = keyword + "," + requestKeywordList[0] + "," + requestKeywordList[requestKeywordList.length - 1];
} else {
keywords = keyword + "," + keywords;
}
} else {
keywords = keyword;
}
searchESstr = "{"from":0,"size":200,"query":{"bool":{"should":[{"match":{"keyword":{"query":"" + keyword + "","boost":2}}},{"match":{"text":{"query":"" + keywords + "","boost":1}}}]}},"track_scores":"true","sort":[{"weight":{"order":"desc"}},{"_score":{"order":"desc"}}]}";
} else if (title != null && title.replace(" ", "").length() > 0) {
searchESstr = "{"from":0,"size":200,"query":{"bool":{"should":{"match":{"text":{"query":"" + title + ""}}}}},"track_scores":"true","sort":[{"weight":{"order":"desc"}},{"_score":{"order":"desc"}}]}";
} else {
// TODO 同上
searchESstr = "";
}
if (searchESstr == "") {
resJsonStr = "{"msg":"请求参数非法!BAD_REQUEST!"}";
} else {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
long timeMillisSpent = 0;
long startEsSearch = System.currentTimeMillis();
long endEsSearch;
// TODO 硬代码
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials("elastic", "123"));
RestClient restClient = RestClient.builder(new HttpHost("es-cn-mp90abc000c3ats.elasticsearch.aliyuncs.com", 9200))
.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
@Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
}
}).build();
try {
HttpEntity httpEntity = new StringEntity(searchESstr, ContentType.APPLICATION_JSON);
//search a document
String endpoint = "/search_text/_search?";
Response response = restClient.performRequest("GET", endpoint,
Collections.singletonMap("pretty", "false"), httpEntity);
endEsSearch = System.currentTimeMillis();
searchESTimeMillisSpent = endEsSearch - startEsSearch;
resJsonStr = EntityUtils.toString(response.getEntity());
} catch (IOException e) {
e.printStackTrace();
resJsonStr = "{"msg":"" + e.toString() + ""}";
httpStatus = HttpStatus.REQUEST_TIMEOUT;
} finally {
credentialsProvider.clear();
restClient.close();
}
}
Thread t = Thread.currentThread();
String debug = ManagementFactory.getRuntimeMXBean().getName() + ":currentT->" + t.getName() + ":t->" + title + ":kw->" + keyword + ":kws->" + keywords;
jsonObject = JSONObject.parseObject(resJsonStr);
long beforeReturn = System.currentTimeMillis();
System.out.println("searchES:" + searchESTimeMillisSpent + "inMethod2BeforeReturn:" + (beforeReturn - startMethod) + "thread:" + debug);
return new ResponseEntity<JSONObject>(jsonObject, httpStatus);
}
@GetMapping("/getVisitedPage/{encryptedId}/{rowCount}/{offset}")
public ResponseEntity getVisitedPage(@PathVariable int encryptedId, @PathVariable int rowCount, @PathVariable int offset) throws IOException {
String debug = this.getClass().getName() + Thread.currentThread().getStackTrace()[1].getMethodName();
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, "ad_detail_page");
SqlSession session = sqlSessionFactory.openSession();
List<PageVisitor> pageVisitors;
String s = "";
try {
RowBounds rowBounds = new RowBounds(offset, rowCount);
pageVisitors = session.selectList("PageVisitor.getPageVisitor", encryptedId, rowBounds);
for (PageVisitor pageVisitor : pageVisitors) {
s += pageVisitor.getUrl();
}
session.commit();
} finally {
session.close();
}
JSONObject jsonObject = JSONObject.parseObject("{"totalPv":"123","debug":"" + debug + "","List<PageVisitor> pageVisitors":"" + s + ""}");
return new ResponseEntity<JSONObject>(jsonObject, HttpStatus.OK);
}
}
spring boot rest api
mybaits limit 传参