Mybatis做ORM映射和 实体映射。
之前有讲过,这里用一个实例去说明一下
Mybatis原理上是。
首先mybatis配置有两点需要注意
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
四个属性
第一个属性driver 驱动加载 包 com.mysql.jdbc.Driver 驱动为mysql驱动
第二个属性 url 值 连接属性
jdbc 连接端口 本机3306 /数据库
useUnicode=true&characterEncoding=utf-8这个比较关键 统一编码 utf-8
否则会出现乱码问题。
<select id="getListFor1" resultType="com.jeesite.modules.test.entity.SearchFpjj">
<!-- #包含项目名,用户代码,项目代码,负责人,以及任务数量-->
SELECT
user_name,
js_sys_user.user_code as xm_zxr,
xm_name as xmname,
project_id as projectid,
js_sys_user.user_code as user_code,
count(czr) as rws ,sum(rwgs) as gs,
sum(IF(rwpj = 120, 1, 0)) AS source1,
sum(IF(rwpj = 100, 1, 0)) AS source2,
sum(IF(rwpj = 80, 1, 0)) AS source3,
sum(IF(rwpj = 60, 1, 0)) AS source4,
sum(IF(rwpj = 40, 1, 0)) AS source5,
(sum(IF(rwpj = 120*rwgs, 120, 0))/sum(rwgs)
+sum(IF(rwpj = 100, 100*rwgs, 0))/sum(rwgs)
+sum(IF(rwpj = 80, 80*rwgs, 0))/sum(rwgs)
+sum(IF(rwpj = 60, 60*rwgs, 0))/sum(rwgs)
+sum(IF(rwpj = 40, 40*rwgs, 0))/sum(rwgs)) as sum_num
FROM
rwgl_que_ans,project_task_tree,project_xmlx_base,js_sys_user
where rwid = project_task_tree.tree_code
and project_xmlx_base.id=project_task_tree.project_id
and rwgl_que_ans.czr=js_sys_user.user_code
GROUP BY project_id,czr
</select>
getListFor1作为ID 类似于前端的ID选择器,就默认是这样吧。
返回类型resultType 相对路径,指定类名。类对象为
package com.jeesite.modules.test.entity;
public class SearchFpjj {
private static final long serialVersionUID = 1L;
private String projectid; // project_id 项目ID
private String xmZxr; // xm_zxr 人员ID
private String userName; //项目名称
private String xmname ; //项目名
private String usercode;//执行者code
private Long xm_RewardNum; // xm_rewardnum 分配系数
private int rws; //任务数
private int gs; //工时
private int source1; //120分数
private int source2; //100分数
private int source3; //80分数
private int source4; //60分数
private int source5; //40分数
private int sum_num; //计分
private int tj_tj; //推荐系数 ??
}
有一个有意思的现象
往往mybatis会有这样的配置
<!-- 使用驼峰命名法转换字段。 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
可以看到对应关系中的两个字段
user_code 对应类的 usercode 下划线直接忽略了。亲测可以取到对象。驼峰的命名加上下划线就不好处理了。
数据库字段:
is_man
bean:
private Integer isMan;
mapper配置不需要写字段与属性的配置,会自动映射。
基本会符合这样一个设定
说的是驼峰,可是实际上似乎是用不用驼峰只要字匹配的属性都行。
在做mysql的调试的时候如果需要用到数据库可视化软件。
简单的说一下数据可视化的概念,就是让数据库的数据你能看到。
不是简单的show table这样去看。而是借助工具,更简单的看到。
Navicat
https://www.cnblogs.com/liujiacai/p/11582000.html
可借鉴网址
亲测有效。
Mybatis原理篇
所有的框架都是基于基本的23种设计模式衍生而来,从代码设计带接口设计,最终实现的目的就是适配。
让我们后来开发的人能够用尽可能少的代码去将原本复杂的配置跑起来。从而实现简化开发成本,不要多走弯路,但是如果你对底层没有一点了解,那么在后期开发的过程中,如果遇到瓶颈和优化的问题,那么将会面临很大的窘境,所以我非常拒绝真正的黑匣子。
前文有说到过对象
sqlSessionFactory
下面来看看他的配置
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:configuration.xml"></property>
<property name="mapperLocations" value="classpath:com/xxx/mybatis/mapper/*.xml"/>
<property name="typeAliasesPackage" value="com.tiantian.mybatis.model" />
</bean>
不难看出这里配置了一堆路径
Com 指代包
classpath 指代指定路径
mapperLocations这个属性即是指代路径下所有的mapper
读取方式如java
buildSqlSessionFactory.java
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
if (logger.isDebugEnabled()) {
logger.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
}
这就是使用来解析mapper类的源码。通过读取配置文件,获取指定的mapper所在路径,然后读取解析。
毕竟既然要做到适用的程度,就必须按照一定规范。不然没有可推广性。
配置系统实例化buildSqlSessionFactory.java 在虚拟机中跑起来
```java
public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
super(configuration);
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
this.parser = parser;
this.sqlFragments = sqlFragments;
this.resource = resource;
}
系统会调用xmlMapperBuilder的parse方法解析mapper,方法内容如下
public void parse() {
//如果configuration对象还没加载xml配置文件(避免重复加载,实际上是确认是否解析了mapper节点的属性及内容,
//为解析它的子节点如cache、sql、select、resultMap、parameterMap等做准备),
//则从输入流中解析mapper节点,然后再将resource的状态置为已加载
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
//解析在configurationElement函数中处理resultMap时其extends属性指向的父对象还没被处理的<resultMap>节点
parsePendingResultMaps();
//解析在configurationElement函数中处理cache-ref时其指向的对象不存在的<cache>节点(如果cache-ref先于其指向的cache节点加载就会出现这种情况)
parsePendingChacheRefs();
//同上,如果cache没加载的话处理statement时也会抛出异常
parsePendingStatements();
}
具体类都封在mabtis的jar包中。
我们经常都会看到mapper文件,下面哪一个mapper文件的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="com.jeesite.modules.test.dao.TestDataDao">
<!-- 查询数据
<select id="findList" resultType="TestData">
SELECT ${sqlMap.column.toSql()}
FROM ${sqlMap.table.toSql()}
<where>
${sqlMap.where.toSql()}
</where>
ORDER BY ${sqlMap.order.toSql()}
</select> -->
<!-- 演示Map参数和返回值,支持分页 -->
<select id="findListForMap" resultType="map">
SELECT * FROM test_data a
<where>
<if test="testInput != null and testInput != ''">
AND a.test_input = #{testInput}
</if>
</where>
<if test="page != null and page.orderBy != null and page.orderBy != ''">
ORDER BY ${page.orderBy}
</if>
</select>
<select id="getListFor" resultType="com.jeesite.modules.test.entity.TestDemo">
<!-- #包含项目名,用户代码,项目代码,负责人,以及任务数量-->
SELECT project_id,xm_rewardNum,xm_zxr from xm_fpjj
</select>
<select id="getListFor1" resultType="com.jeesite.modules.test.entity.SearchFpjj">
<!-- #包含项目名,用户代码,项目代码,负责人,以及任务数量-->
SELECT
user_name,
js_sys_user.user_code as xm_zxr,
xm_name as xmname,
project_id as projectid,
js_sys_user.user_code as user_code,
count(czr) as rws ,sum(rwgs) as gs,
sum(IF(rwpj = 120, 1, 0)) AS source1,
sum(IF(rwpj = 100, 1, 0)) AS source2,
sum(IF(rwpj = 80, 1, 0)) AS source3,
sum(IF(rwpj = 60, 1, 0)) AS source4,
sum(IF(rwpj = 40, 1, 0)) AS source5,
(sum(IF(rwpj = 120*rwgs, 120, 0))/sum(rwgs)
+sum(IF(rwpj = 100, 100*rwgs, 0))/sum(rwgs)
+sum(IF(rwpj = 80, 80*rwgs, 0))/sum(rwgs)
+sum(IF(rwpj = 60, 60*rwgs, 0))/sum(rwgs)
+sum(IF(rwpj = 40, 40*rwgs, 0))/sum(rwgs)) as sum_num
FROM
rwgl_que_ans,project_task_tree,project_xmlx_base,js_sys_user
where rwid = project_task_tree.tree_code
and project_xmlx_base.id=project_task_tree.project_id
and rwgl_que_ans.czr=js_sys_user.user_code
GROUP BY project_id,czr
</select>
</mapper>
解读xml文件的方式有很多种,如果有了解html解析的朋友应该会比较熟悉。将每两个
<?></?>
读取成一种元素,也可以认为这是一种规范。如果不这么去做,是很难写出大量可复用的代码的。那么可能你我都只能停留在不断地写sql然后每个人都需要去思考如何使用各种设计模式去设计架构,那么信息技术发展的瓶颈就是我们能活多久。
所以这种方式就必须采用,并需要去学会使用javabean等一系列规范,即方便个人读写,也能够照顾到信息大方向的发展。
下面是解析mapper过程,看文件结构不难想象其实就是一个xml树解析,如果非要个人去解析,无非是一堆字符串操作,整个文件读进来,再按层解析。不是你我做不到,而是了解的代价,比制作容易的多,生活没有给我们太多时间。下面看解析过程
private void configurationElement(XNode context) {
try {
//获取mapper节点的namespace属性
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
//设置当前namespace
builderAssistant.setCurrentNamespace(namespace);
//解析mapper的<cache-ref>节点
cacheRefElement(context.evalNode("cache-ref"));
//解析mapper的<cache>节点
cacheElement(context.evalNode("cache"));
//解析mapper的<parameterMap>节点
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//解析mapper的<resultMap>节点
resultMapElements(context.evalNodes("/mapper/resultMap"));
//解析mapper的<sql>节点
sqlElement(context.evalNodes("/mapper/sql"));
//使用XMLStatementBuilder的对象解析mapper的<select>、<insert>、<update>、<delete>节点,
//mybaits会使用MappedStatement.Builder类build一个MappedStatement对象,
//所以mybaits中一个sql对应一个MappedStatement
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
语句和连接器,发送器,以及数据集获取,就是数据连接的四步。
现在语句的解析出来了。那么连接?发送?以及结果集?
上述configurationElement能够帮我们拿到所有的完整语句,将它送给Configuration对象,提供给sqlSessionFactory调用。也就是写好的mapper会直接实例化到虚拟机中,你用和不用他就在哪里。
而sqlsession里则封装了相关的内容。具体可以参考下类
public void parseStatementNode() {
//ID属性
String id = context.getStringAttribute("id");
//databaseId属性
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
//fetchSize属性
Integer fetchSize = context.getIntAttribute("fetchSize");
//timeout属性
Integer timeout = context.getIntAttribute("timeout");
//parameterMap属性
String parameterMap = context.getStringAttribute("parameterMap");
//parameterType属性
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
//resultMap属性
String resultMap = context.getStringAttribute("resultMap");
//resultType属性
String resultType = context.getStringAttribute("resultType");
//lang属性
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
Class<?> resultTypeClass = resolveClass(resultType);
//resultSetType属性
String resultSetType = context.getStringAttribute("resultSetType");
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
//是否是<select>节点
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
//flushCache属性
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
//useCache属性
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
//resultOrdered属性
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// Include Fragments before parsing
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
//resultSets属性
String resultSets = context.getStringAttribute("resultSets");
//keyProperty属性
String keyProperty = context.getStringAttribute("keyProperty");
//keyColumn属性
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
//useGeneratedKeys属性
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? new Jdbc3KeyGenerator() : new NoKeyGenerator();
}
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
有没有很熟悉什么resultset result type property等等。就是手动实例化类,不适用驼峰命名映射。
而使用
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="username"/>
<result property="password" column="password"/>
</resultMap>
手动将类中属性和数据库表对应的写法。优缺点也不好说。
数据库这里还是建议多看看数据库逻辑查询,提升内驱力。Mybatis其实了解的差不多就好了。如果真到了mabatis进一步优化的地步。我估计也悬。
再提及一下
POJO(Plain Old Java Object)
无继承,无实现,当然也不能有注解,纯手写的一个java类你都可以叫pojo这就是一个名字。名字而已。
至于一定要纠结具体到点的定义我觉得没必要,因为本来就告诉你是一个简单的类,你偏偏要复杂处理。你随便写一个
Class A{
Private int a;
Set get()
Sum a()
{
a=a+a;
}
}
有属性,有逻辑,有结构,这就是pojo了。不要过分的花时间去记住这些概念。知道是那么回事就好了。有时候我想,是不是起名字的人都没想好,这些是啥。只是一个区分。就好像球类和篮球一样。知道篮球是篮球就好了。写代码的人,关注代码规范,和代码逻辑就好了。上述简介够用了。
辅助理解:POJO可以认为是一个中间对象:
一个中间对象,可以转化为PO、DTO、VO。
1 .POJO持久化之后==〉PO(persistent object)ORM 实体映射模型,就是你的对象和数据库如何对应起来。由虚拟机保存。一一对应和实例化是重点。(在运行期,由Hibernate中的cglib动态把POJO转换为PO,PO相对于POJO会增加一些用来管理数据库entity状态的属性和方法。PO对于programmer来说完全透明,由于是运行期生成PO,所以可以支持增量编译,增量调试。)
2 .POJO传输过程中==〉DTO Data Transfer Object 数据传输对象。将所需要的的数据传输出去临时构建的对象或者json。以一种约定的形式发送给远程主机。
3 .POJO用作表示层==〉VO view object显示对象。就是你需要显示啥就拿啥造一个逻辑也好,物理也好的对象。
也很好理解,写一个类你总要用它来干啥吧。根据你的用途在给它起个名字。
ADO data access object 数据访问对象 单例模式和多例模式,你用数据库不可能一直实例化,所以大部分的内容都是由实例化的连接提供服务,以前还需要用线程池,多例模式,甚至是工厂模型来实现。
详情参考
https://blog.csdn.net/chenchunlin526/article/details/69939337
JavaBean
Javabean的约定
1、这个类必须具有一个公共的(public)无参构造函数;
2、所有属性私有化(private);
3、私有化的属性必须通过public类型的方法(getter和setter)暴露给其他程序,并且方法的命名也必须遵循一定的命名规范。
4、这个类应是可序列化的。(比如可以实现Serializable 接口,用于实现bean的持久性)
看这四条,第一可以动态实例化,第二,可以通过反射类结构去读取指定类属性,第四条,是保障在传输过程的可恢复。什么叫可恢复,类似于逻辑电路里的校验码。确认传输过程数据不丢失,如果丢失重新请求的一个字段。这是给虚拟机提供的,具体过程也是封装的死死的。
之前说了pojo是一个简单地类,现在这个类复杂了,就可以认为这是一个javabean,非得再起个名字,好区分,毕竟约定中,也没说javabean一定要继承啥。他可以认为是一种规范,你可以用这种规范去做任何类构造,类似于tcp-ip协议,遵循指定的内容,指定的加密解密,常人看来很繁琐,不这么繁琐,难道你敢在互联网裸奔。用手机,你旁边的人,完全知道你在干嘛。
补充一下数据库约束内容
https://www.cnblogs.com/willingtolove/p/9215330.html