再学习之MyBatis
一、框架基本介绍
1、概念
支持普通SQL查询、存储过程和高级映射,简化和实现了Java 数据持久化层的的开源框架,主要流行的原因在于他的简单性和易使用性。
2、特点
持久层 、ORM(对象关系映射) 、轻量级、支持SQL语句。
3、工作原理
- 应用程序读取mybatis的配置文件,形成配置对象。
- 通过sqlsessionfactorybuilder读取配置对象,产生sqlsessionfactory
- 通过sqlsessionfactory得到sqlsession
- 通过sqlsession得到mapper映射器
- 通过mapper读取对应的映射文件从而操作数据库
- 处理事务
- 释放sqlsession
二、配置文件含义
1、environment
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="root" />
<property name="password" value="admin" />
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="JNDI">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="root" />
<property name="password" value="admin" />
</dataSource>
</environment>
</environments>
主要用来配置多个dataSource环境;在同一个环境中切换不同的dataSource时,需要为每个数据库创建一个SqlSessionFactory。对于每个环境 environment,我们需要配置 dataSource 和 transactionManager。
1.1 DateSource
- UNPOOLED:为每一个数据库操作创建一个新的连接,使用结束就关闭他。该方式适用于小规模数量的并发用户的简单应用程序上。
- POOLED:Mybatis会创建一个数据库连接池,连接池中的一个连接将会被用作数据库操作。一旦数据库操作完成,MyBatis 会将此连接返回给连接池。
- JNDI:MyBatis 从在应用服务器向配置好的 JNDI 数据源 dataSource 获取数据库连接。
1.2 TransactionManager
- JDBC:应用程序自己管理事务(Tomcat)
- MANAGED:应用本身不去管理事务,而把事务管理交给应用所在的服务器进行管理。(JBoss、WebLogic、GlassFish)
2、properties
<properties resource="application.properties">
<property name="jdbc.username" value="db_user" />
<property name="jdbc.password" value="verysecurepwd" />
</properties>
这里,如果 application.properties 文件包含值 jdbc.username 和 jdbc.password,则上述定义的 username 和password 的值 db_user 和 verysecurepwd 将会被 application.properties 中定义的对应的 jdbc.username 和jdbc.password 值覆盖。
3、typeAliases
如果没有设置typeAliases的类型别名,对于resultType和parameterType属性值,我们需要使用JavaBean的完全限定名。所以,我们可以为完全限定名使用别名,通过typeAliases设置:
3.1 为每个JavaBean单独起别名
<typeAliases>
<typeAlias alias="Student" type="com.mybatis3.domain.Student" />
<typeAlias alias="Tutor" type="com.mybatis3.domain.Tutor" />
</typeAliases>
3.2 为JavaBean 所在的包起别名
当然也可以不用为每一个JavaBean 单独定义别名,你可以为提供需要起别名的JavaBean 所在的包,Mybatis会自动扫描包内定义的JavaBeans,然后分别为JavaBean注册一个小写字母开头的非完全限定的类名形式的别名。
<typeAliases>
<package name="com.mybatis3.domain" />
</typeAliases>
如果 Student.java 和 Tutor.java 的 Bean 定义在 com.mybatis3.domain 包中,则 com.mybatis3.domain.Student的别名会被注册为 student。而 com.mybatis3.domain.Tutor 别名将会被注册为 tutor。
3.3 利用注解的方式起别名
使用注解@Alias起别名,@Alias 注解将会覆盖配置文件中的<typeAliases>定义:
@Alias("StudentAlias")
public class Student
{ }
4、typeHandlers
MyBatis 对于以下的类型使用内建的类型处理器:所有的基本数据类型、基本类型的包装类型、 byte[]、java.util.Date、 java.sql.Date、 java,sql.Time、 java.sql.Timestamp、 java 枚举类型等。所以当 MyBatis 发现属性的类型属于上述类型,他会使用对应的类型处理器将值设置到PreparedStatement 中,同样地,当从 SQL 结果集构建 JavaBean 时,也有类似的过程。
如果,我们可以自定义一个类型处理器为我们子定义的Class服务。一旦我们实现了自定义的类型处理器,我们需要在 mybatis-config.xml 中注册它:
*/
public class PhoneTypeHandler extends BaseTypeHandler<PhoneNumber> {
public void setNonNullParameter(PreparedStatement preparedStatement, int i, PhoneNumber phoneNumber, JdbcType jdbcType) throws SQLException {
preparedStatement.setString(i, phoneNumber.toString());
} public PhoneNumber getNullableResult(ResultSet resultSet, String s) throws SQLException {
return new PhoneNumber(resultSet.getString(s));
} public PhoneNumber getNullableResult(ResultSet resultSet, int i) throws SQLException {
return new PhoneNumber(resultSet.getString(i));
} public PhoneNumber getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
return new PhoneNumber(callableStatement.getString(i));
}
}
自定义类型处理器
<typeHandlers>
<typeHandler handler="com.summersoft.ts.util.PhoneTypeHandler"/>
</typeHandlers>
注册自定义类型处理器
时间类型:Mybatis 会将java.util.Data 类型转换成java.sql.Timestamp(时间戳)并设置。
5、Mappers
<mappers>
<mapper resource="com/mybatis3/mappers/StudentMapper.xml" />
<mapper url="file:///D:/mybatisdemo/app/mappers/TutorMapper.xml" />
<mapper class="com.mybatis3.mappers.TutorMapper" />
<package name="com.mybatis3.mappers" />
</mappers>
- resource 属性用来指定在 classpath 中的 mapper 文件。
- url 属性用来通过完全文件系统路径或者 web URL 地址来指向 mapper 文件
- class 属性用来指向一个 mapper 接口
- package 属性用来指向可以找到 Mapper 接口的包名
6、Settings
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。下表描述了设置中各项的意图、默认值等。
设置参数 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 该配置影响的所有映射器中配置的缓存的全局开关。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。 | true | false | false |
aggressiveLazyLoading | 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods). | true | false | false (true in ≤3.4.1) |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集(需要兼容驱动)。 | true | false | true |
useColumnLabel | 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。 | true | false | true |
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。 | true | false | False |
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
autoMappingUnknownColumnBehavior | 指定发现自动映射目标未知列(或者未知属性类型)的行为。
|
NONE, WARNING, FAILING | NONE |
defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数。 | 任意正整数 | Not Set (null) |
defaultFetchSize | 为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。 | 任意正整数 | Not Set (null) |
safeRowBoundsEnabled | 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false。 | true | false | False |
safeResultHandlerEnabled | 允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false。 | true | false | True |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 | true | false | False |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。 | SESSION | STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 | JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER | OTHER |
lazyLoadTriggerMethods | 指定哪个对象的方法触发一次延迟加载。 | A method name list separated by commas | equals,clone,hashCode,toString |
defaultScriptingLanguage | 指定动态 SQL 生成的默认语言。 | A type alias or fully qualified class name. | org.apache.ibatis.scripting.xmltags.XMLLanguageDriver |
defaultEnumTypeHandler | Specifies the TypeHandler used by default for Enum. (Since: 3.4.5) | A type alias or fully qualified class name. | org.apache.ibatis.type.EnumTypeHandler |
callSettersOnNulls | 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null 的。 | true | false | false |
returnInstanceForEmptyRow | 当返回行的所有列都是空时,MyBatis默认返回null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集 (i.e. collectioin and association)。(从3.4.2开始) | true | false | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀。 | Any String | Not set |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | Not set |
proxyFactory | 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。 | CGLIB | JAVASSIST | JAVASSIST (MyBatis 3.3 or above) |
vfsImpl | 指定VFS的实现 | 自定义VFS的实现的类全限定名,以逗号分隔。 | Not set |
useActualParamName | 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,并且加上-parameters选项。(从3.4.1开始) | true | false | true |
configurationFactory | 指定一个提供Configuration实例的类. 这个被返回的Configuration实例是用来加载被反序列化对象的懒加载属性值. 这个类必须包含一个签名方法static Configuration getConfiguration(). (从 3.2.3 版本开始) | 类型别名或者全类名. | Not set |
三、映射文件讲解
我们通过映射器配置文件配置了映射语句,同时创建一个完全对应的一个映射器接口。接口名跟配置文件名相同。接口所在包名也跟配置文件所在包名完全一 样。在配置文件中,其命名空间namespace 应该和接口的完全限定名保持一致。
1、INSERT
id: 对应接口的方法名
parameterType: 输入参数
useGeneratedKeys="true" :让数据库生成自增长的列
keyProperty="属性名": 将生成的值设置到其中一个输入对象属性内
如果是Oracle数据库,没有像MySQL那样的自增机制:
<insert id="insertStudent" parameterType="Student">
<selectKey keyProperty="studId" resultType="int" order="BEFORE">
SELECT ELEARNING.STUD_ID_SEQ.NEXTVAL FROM DUAL
</selectKey>
INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL, PHONE)
VALUES(#{studId},#{name},#{email},#{phone})
</insert>
order="BEFORE" 表示在插入语句发生前产生studId的值就赋给插入语句。
<insert id="insertStudent" parameterType="Student">
INSERT INTO STUDENTS(NAME,EMAIL, PHONE)
VALUES(#{name},#{email},#{phone})
<selectKey keyProperty="studId" resultType="int" order="AFTER">
SELECT ELEARNING.STUD_ID_SEQ.CURRVAL FROM DUAL
</selectKey>
</insert>
order="AFTER" 表示在插入语句之后采用触发器(trigger)来设置主键值
2、SELECT
2.1、 resultType='Student' 当 column名和对象property名一致时,会自动把相应的column填充上对象的property,如果查询记录返回多条,Mybatis 自动用 集合类来接收。
2.2、Mybatis根据集合的类型,会采用适当的集合实现:
对于 List,Collection,Iterable 类型,MyBatis 将返回 java.util.ArrayList
对于 Map 类型,MyBatis 将返回 java.util.HashMap
对于 Set 类型,MyBatis 将返回 java.util.HashSet
对于 SortedSet 类型,MyBatis 将返回 java.util.TreeSet
2.3、ResultType 和 ResuleMap 不能同时使用,ResultMap的 id 在此命名空间内是唯一的
2.4、Resulttype="java.util.HashMap"。在这种情况下,结果集中的列名将会作为Map中的key值,而列值作值将会作为Map的value值。如果查询记录返回多条,Mybatis自动用集合类来接收。
2.5、我们可以从另外一个<resultMap>,扩展出一个新的<resultMap>,这样原来的属性值可以扩展过来
2.6、一对一关联查询
<resultMap type="Student" id="StudentWithAddressResult">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<result property="phone" column="phone" />
<result property="address.addrId" column="addr_id" />
<result property="address.street" column="street" />
<result property="address.city" column="city" />
<result property="address.state" column="state" />
<result property="address.zip" column="zip" />
<result property="address.country" column="country" />
</resultMap>
一对一直接关联
<resultMap type="Address" id="AddressResult">
<id property="addrId" column="addr_id" />
<result property="street" column="street" />
<result property="city" column="city" />
<result property="state" column="state" />
<result property="zip" column="zip" />
<result property="country" column="country" />
</resultMap>
<resultMap type="Student" id="StudentWithAddressResultIn">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<association property="address" resultMap="AddressResult" />
</resultMap>
一对一嵌套关联
<resultMap type="Student" id="StudentWithAddressResultHasOne">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<association property="address" javaType="Address">
<id property="addrId" column="addr_id" />
<result property="street" column="street" />
<result property="city" column="city" />
<result property="state" column="state" />
<result property="zip" column="zip" />
<result property="country" column="country" />
</association>
</resultMap>
一对一内联关联has one
2.7、一对多关联查询
<resultMap type="Course" id="CourseResult">
<id column="course_id" property="courseId" />
<result column="name" property="name" />
<result column="description" property="description" />
<result column="start_date" property="startDate" />
<result column="end_date" property="endDate" />
</resultMap>
<resultMap type="Tutor" id="TutorResult">
<id column="tutor_id" property="tutorId" />
<result column="tutor_name" property="name" />
<result column="email" property="email" />
<collection property="courses" resultMap="CourseResult" />
</resultMap>
2.8、在MyBatis的配置文件中,可以不指定输入参数。而采用#{param1}和#{param2}引用接口中方法的形参。
3、动态SQL
3.1 if 条件
<select id="searchCourses" parameterType="hashmap" resultMap="CourseResult">
SELECT * FROM COURSES
WHERE TUTOR_ID= #{tutorId}
<if test="courseName != null">
AND NAME LIKE #{courseName}
</if>
<if test="startDate != null">
AND START_DATE > #{startDate}
</if>
<if test="endDate != null">
AND END_DATE < #{endDate}
</if>
</select>
if 语句
3.2 choose when条件
<select id="searchCoursesTwo" parameterType="hashmap" resultMap="CourseResult">
SELECT * FROM COURSES
<choose>
<when test="searchBy == 'Tutor'">
WHERE TUTOR_ID= #{tutorId}
</when>
<when test="searchBy == 'CourseName'">
WHERE name like #{courseName}
</when>
<otherwise>
WHERE TUTOR start_date >= now()
</otherwise>
</choose>
</select>
choose when 条件
<choose>测试条件的值,且使用第一个值为TRUE的子句,如果没有条件为True,则使用<otherwise>内的字句
3.3 where 条件
<select id="searchCourses" parameterType="hashmap"
resultMap="CourseResult">
SELECT * FROM COURSES
<where>
<if test=" tutorId != null ">
TUTOR_ID= #{tutorId}
</if>
<if test="courseName != null">
AND name like #{courseName}
</if>
<if test="startDate != null">
AND start_date >= #{startDate}
</if>
<if test="endDate != null">
AND end_date <= #{endDate}
</if>
</where>
</select>
Where 子句
<where>元素只有在内部标签有返回内容时才会在动态语句上插入WHERE条件语句。并且,如果Where子句以AND 或者 OR 打头,则打头的AND 或 OR 将会被移除。
3.4 trim 条件
<select id="searchCourses" parameterType="hashmap" resultMap="CourseResult">
SELECT * FROM COURSES
<trim prefix="WHERE" prefixOverrides="AND | OR">
/*拼接成的字句前面加上WHERE,如果WHERE后面有AND 或者 OR,将去掉。suffix末尾加上,suffixOverrides 末尾去掉*/
<if test=" tutorId != null ">
TUTOR_ID= #{tutorId}
</if>
<if test="courseName != null">
AND name like #{courseName}
</if>
</trim>
</select>
trim 子句
<trim> 元素和 <where>元素类似,如果任意一个<if>条件为true,<trim>元素会插入WHERE,并且移除紧跟WHERE 后面的AND 或 OR
3.5 foreach 循环
<select id="searchCoursesByTutors" parameterType="map" resultMap="CourseResult">
SELECT * FROM COURSES
<if test="tutorIds != null">
<where>
<foreach item="tutorId" collection="tutorIds">
OR tutor_id=#{tutorId}
</foreach>
</where>
</if>
</select>
foreach子句
它可以迭代遍历一个数组或者列表,构造AND/OR条件或者一个IN子句。foreach 的参数 open、close、separator(列表中的每个元素以什么分隔)。
3.6 set 条件
<update id="updateStudent" parameterType="Student">
update students
<set>
<if test="name != null">name=#{name},</if>
<if test="email != null">email=#{email},</if>
<if test="phone != null">phone=#{phone},</if>
</set>
where stud_id=#{id}
</update>
set子句
如果<if>条件返回了任何文本内容,<set>将会插入set关键字和其文本内容。并且会剔除末尾的","
4、缓存
4.1、第一级缓存:如果你使用同一个SqlSession 接口对象调用了相同的SELECT语句,则直接会从缓存中返回结果,而不是再查询一次数据库。
4.2、第二级缓存:我们可以在SQL映射器XML配置文件中使用<cache />元素添加全局二级缓存。
- 所有的在映射语句文件定义的<select>语句的查询结果都会被缓存
- 所有的在映射语句文件定义的<insert>,<update> 和<delete>语句将会刷新缓存
- 缓存根据最近最少被使用(Least Recently Used,LRU)算法管理
- 缓存不会被任何形式的基于时间表的刷新(没有刷新时间间隔),即不支持定时刷新机制
- 缓存将存储 1024 个 查询方法返回的列表或者对象的引用
- 缓存是线程安全的
当然我们也可以 复写默认属性来自定义缓存的行为:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
eviction:LRU、FIFO、SOFT、WEAK
readOnly:一个只读的缓存会向调用者返回被缓存对象的一份引用。一个读/写缓存cache将会返回改对象的一份拷贝。
四、其他
1. Mybatis 的查询结果集对属性 set 的时候,如果子类和父类有相同的属性,会调用谁的 setter 方法呢?
答:会调用父类的 setter 方法。所以调用子类的相同相同属性 getter 方法,会导致 get 到的数据 为 null。这也体现了继承中的思想。属性只会被隐藏,不会被重写。方法相反,不会被隐藏,只会被重写。
2. 通过对MyBatis源码进行分析,查询函数返回的列表和数据项都不为空,在代码中可以不用进行空指针判断。
再学习之MyBatis的更多相关文章
- (转)MyBatis框架的学习(二)——MyBatis架构与入门
http://blog.csdn.net/yerenyuan_pku/article/details/71699515 MyBatis框架的架构 MyBatis框架的架构如下图: 下面作简要概述: S ...
- js再学习笔记
#js再学习笔记 ##基本 1.js严格区分大小写 2.js末尾的分号可加,也可不加 3.六种数据类型(使用typeof来检验数据的类型) `typeof` - undefined: `var ...
- express再学习
对比spring,django,再学习express就有很多共通的地方啦... 看的书是一本小书,<express in action>,排版比较好. 昨天开始看,看了快四分之一啦... ...
- Android再学习-20141023-Intent-Thread
20141023-Android再学习 Intent对象的基本概念 Intent是Android应用程序组件之一 Intent对象在Android系统中表示一种意图 Intent当中最重要的内容是ac ...
- Android再学习-20141022-Activity的生命周期
20141022-Android再学习 如何在一个应用程序当中定义多个Activity 定义一个类,继承Activity 在该类当中,复写Activity当中的onCreate方法.onCreate( ...
- Android再学习-20141018-布局-进度条
20141018-Android再学习 对齐至控件的基准线 为了保证印刷字母的整齐而划定的线(四线三格的第三条线). android:layout_alignBaseline 与父控件的四个边缘对齐( ...
- 再学习sqlhelper
在机房收费重构系统的时候,第一次学习sqlhelper.当时感觉比较简单,没有写博客总结,现在又经过了图书馆的学习,感觉还是有必要写一写的. SqlHelper是一个基于.NETFramework的数 ...
- c/c++再学习:常用字符串转数字操作
c/c++再学习:常用字符串转数字操作 能实现字符串转数字有三种方法,atof函数,sscanf函数和stringstream类. 具体demo代码和运行结果 #include "stdio ...
- c/c++再学习:C与Python相互调用
c/c++再学习:Python调用C函数 Python 调用C函数比较简单 这里两个例子,一个是直接调用参数,另一个是调用结构体 C代码 typedef struct { int i1; int i2 ...
随机推荐
- SQL server2005学习笔记(一)数据库的基本知识、基本操作(分离、脱机、收缩、备份、还原、附加)和基本语法
在软件测试中,数据库是必备知识,假期闲里偷忙,整理了一点学习笔记,共同探讨. 阅读目录 基本知识 数据库发展史 数据库名词 SQL组成 基本操作 登录数据库操作 数据库远程连接操作 数据库分离操作 数 ...
- Ansible(三) - playbook简介
Ⅰ. Playbook介绍 Playbook其实就是ansible的一个任务列表,各任务按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个任务后再开始第二个.在顺序运行某playb ...
- vue2购物车ch4-(筛选v-for 点击的那个设置样式 设为默认地址其他 联动 非循环的列表选中和非选中 删除当前选中的列表)
1 address.html <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...
- 使用siege对web接口进行post方式的压力测试
为了达到压力测试的效果,需要申请一台线上机器,并且安装压力测试的工具siege. 安装新版siege.资料说yum安装的版本2.70对于post方式支持的不好,验证后发现请求可以正常发过去,但是打开d ...
- 【MVC】会员注册/登录,普通验证,会员名是否注册Ajax验证以及会员邮件验证实现原理
using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentM ...
- 浅谈Java接口
接口(英文:Interface)是Java中非常重要的内容,初学的时候可能感受不深,但是在做项目的时候,对面向接口编程的运用就变得尤为重要,不过这是后话了.现在先讨论假如是刚刚接触接口这个概念,该怎么 ...
- 分布式锁的实现(redis)
1.单机锁 考虑在并发场景并且存在竞态的状况下,我们就要实现同步机制了,最简单的同步机制就是加锁. 加锁可以帮我们锁住资源,如内存中的变量,或者锁住临界区(线程中的一段代码),使得同一个时刻只有一个线 ...
- Socket简单实现数据交互及上传
声明:本文为原创,如需转载请说明出处:http://www.cnblogs.com/gudu1/p/7669175.html 首先为什么要写这个呢?因为在几个月之前我还使用Socket做一个小项目,而 ...
- iOS的异步绘制--YYAsyncLayer源码分析
iOS的异步渲染 最近看了YYAsyncLayer在这里总结一下.YYAsyncLayer是整个YYKit异步渲染的基础.整个项目的Github地址在这里.你可以先下载了一睹为快,也可以跟着我一步一步 ...
- JS中的作用域以及全局变量的问题
一. JS中的作用域 1.全局变量:函数外声明的变量,称为全部变量 局部变量:函数内部使用var声明的变量,称为局部变量在JS中,只有函数作用域,没有块级作用域!!!也就是说,if/for等有{}的结 ...