MyBatis魔法堂:即学即用篇
一、前言
本篇内容以理解MyBatis的基本用法和快速在项目中实践为目的,遵循Make it work,better and excellent原则。 技术栈为MyBatis3.2.7+log4j1.2.17+sqlite3+jdk1.7。
二、示例
示例代码功能:
学校人员管理系统,对象分别为学生、教师和班级,学生与班级为多对一关系,班级与教师为一对多关系。示例代码主要为操作学生对象。
1. 表结构
学生表(TStudentML)
字段 | 数据类型 | 备注 |
ID | Integer | 主键 |
Name | Text | 姓名 |
ClassID | Integer | 班级ID |
班级表(TClassML)
字段 | 数据类型 | 备注 |
ID | Integer | 主键 |
Name | Text | 姓名 |
教师表(TTeacherML)
字段 | 数据类型 | 备注 |
ID | Integer | 主键 |
Name | Text | 姓名 |
ClassID | Integer | 班级ID |
2. 项目目录结构
src
|-- com
| |-- entity
| | |-- ETeacher
| | |-- EStudent
| | |-- EClass
| |-- dao
| |-- studentMapper.xml
| |-- StudentMapper.java
| |-- DBase.java
| |-- DStudent.java
|-- mybatis-config.xml
|-- db.db
|-- log4j.properties
|-- config.properties
3. 数据库配置文件(config.properties)
driver=org.sqlite.JDBC
url=jdbc:sqlite:src/db.db
4. log4j.properties
log4j.rootLogger=TRACE, studout
log4j.appender.studout=org.apache.log4j.ConsoleAppender
log4j.appender.studout.layout=org.apache.log4j.PatternLayout
log4j.appender.studout.layout.ConversionPattern=[%5p]%t -%m%n
5. mybatis-config.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>
<!--
引入外部properties配置文件,后面内容则通过${属性名}来引用属性值
在使用实例化SqlSessionFactory时,还可以通过new SqlSessionFactoryBuilder.build(config.xml的InputStream实例, Properties实例)来设置属性值。
优先级从高到低是:
1. 通过build方法入参设置
2. 通过resource引入的属性
3. 通过property标签设置的属性
-->
<properties resource="config.properties"></properties>
<!--强制指定MyBatis使用log4j作为日志日志框架,若不指定那么当部署到如Tomcat等应用容器时,会被容器设置为使用common-logging来记录日志-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<!--设置自定义JAVA类型的别名,否则在映射文件中的resultType、parameterType等特性值就需要填写全限定类名才有效-->
<typeAliases>
<!--
这时对于包下的类,在映射文件中的resultType、parameterType等特性值,我们只需写类名或首字母小写的类名
当自定义JAVA类配合@Aliase("别名")使用时,只需写别名即可,且不区分大小写
MyBatis对JAVA原生类型定义了内置别名:
`int`,`long`等就是`_int`,`_long`
`Integer`,`String`就是`int`,`string`
-->
<package name="com.entity"/>
</typeAliases>
<environments default="dev">
<!--运行环境配置-->
<environment id="dev">
<!--
type属性用于指定事务管理器类型
JDBC:使用JDBC的提交和回滚设置,依赖从数据源获取的连接来管理事务范围。
MANAGED:让容器(如Spring)来管理事务的生命周期。默认情况会关闭连接,
若不想关闭连接则需要如下配置:
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
-->
<transactionManager type="JDBC"></transactionManager>
<!--
type属性用于指定连接池类型
UNPOOLED:连接用完就关闭,不放到连接池
POOLED:连接用完则放在连接池
-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<!--虽然sqlite不用填写username和password,但这两个节点必须保留,否则将报错-->
<property name="username" value=""/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<!--向MyBatis注册映射信息-->
<mappers>
<mapper resource="com/dao/studentMapper.xml"></mapper>
</mappers>
</configuration>
6. 实体类
// 教师实体类
public class ETeacher{
private int id;
private String name;
// 省略各种setter和getter.......
} // 班级实体类
public class EClass{
private int id;
private String name;
// 省略各种setter和getter.......
} // 学生实体类
public class EStudent{
private int id;
private String name;
private EClass myClass;
private List<ETeacher> teachers;
// 省略各种setter和getter.......
}
7. 映射接口
public interface StudentMapper{
/**
* 通过姓名获取学生信息(不含教师、班级信息)
* @param name 学生姓名
* @return
*/
EStudent getJustStudentInfo(String name); /**
* 通过姓名模糊查询学生信息(不含教师、班级信息)
* @param name 学生姓名
* @return
*/
List<EStudent> getJustStudentInfos(String name); /**
* 通过姓名和班级名称获取学生信息(含教师、班级信息)
* @param studentName 学生姓名
* @param className 班级名称
* @return
*/
EStudent getStudentInfo(String studentName, String className); /**
* 新增学生信息
* @param student
* @return
*/
void addStudent(EStudent student); /**
* 删除学生信息
* @param id
* @return
*/
void delStudent(int id);
}
8. 映射文件
<?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="main.dao.StudentMapper">
<select id="getJustStudentInfo" resultType="EStudent">
select ID,Name from TStudentML where Name = #{0}
</select> <select id="getJustStudentInfos" resultType="EStudent">
select * from TStudentML
<if test="#{0} != null">
where name like '%'+#{}+'%'
</if>
</select> <select id="getStudentInfo" resultMap="getStudentInfoResultMap">
select
a.ID a_id, a.Name a_name,
b.ID b_id, b.Name b_name,
c.ID c_id, c.Name c_name, c.ClassID c_classId
from
TStudentML a left outer join TClassML b on(a.ClassID=b.ID)
left outer join TTeacherML c on(b.ID=c.ClassID)
<trim prefix="where" prefixOverrides="and">
<if test="#{0} != null">
a.Name like '%'+#{}+'%'
</if>
<if test="#{1} != null">
and b.Name like '%'+#{}+'%'
/if>
</trim>
</select>
<resultMap id="getStudentInfoResultMap" type="EStudent">
<id property="id" column="a_id"/>
<result property="name" column="a_name"/>
<!-- 一对一关系 -->
<association property="myClass" javaType="EClass">
<id property="id" column="b_id"/>
<result property="name" column="b_name"/>
</association>
<!-- 一对多关系 -->
<collection property="teachers" ofType="ETeacher">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<result property="classId" column="c_classId"/>
</collection>
</resultMap> <insert id="addStudent" parameterType="EStudent">
insert into TStudentML(Name, ClassID) values(#{name}, #{myClass.id})
</insert> <delete id="delStudent">
delete from TStudentML where ID = #{0}
</delete>
</mapper>
9. 数据库操作基类
public class DBase{
private static SqlSessionFactory factory = null;
public DBase(){
if (factory != null) return; InputStream is = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
factory = new SqlSessionFactoryBuilder().build(is);
} /**
* 获取SqlSession实例,使用后需调用实例的close()函数释放资源
* @return
*/
protected SqlSession openSession(){
return factory.openSession();
}
}
10. 数据库操作类
public class DStudent extends DBase{
/**
* 通过姓名获取学生信息(不含教师、班级信息)
* @param name 学生姓名
* @return
*/
public EStudent getJustStudentInfo(String name){
EStudent ret = null;
SqlSession session = openSession();
try{
StudentMapper sm = session.getMapper(StudentMapper.class);
ret = sm.getJustStudentInfo(name);
}
finally{
session.close();
} return ret;
} /**
* 通过姓名模糊查询学生信息(不含教师、班级信息)
* @param name 学生姓名
* @return
*/
public List<EStudent> getJustStudentInfos(String name){
List<EStudent> ret = null;
SqlSession session = openSession();
try{
StudentMapper sm = session.getMapper(StudentMapper.class);
ret = sm.getJustStudentInfos(name);
}
finally{
session.close();
} return ret;
} /**
* 通过姓名和班级名称获取学生信息(含教师、班级信息)
* @param name 学生姓名
* @param className 班级名称
* @return
*/
public EStudent getStudentInfo(String name, String className){
EStudent ret = null;
SqlSession session = openSession();
try{
StudentMapper sm = session.getMapper(StudentMapper.class);
ret = sm.getStudentInfo(name, className);
}
finally{
session.close();
} return ret;
} /**
* 新增学生信息
* @param student
*/
public void addStudent(EStudent student){
SqlSession session = openSession();
try{
StudentMapper sm = session.getMapper(StudentMapper.class);
sm.addStudent(student);
session.commit();
}
finally{
session.close();
}
} /**
* 删除学生信息
* @param id
*/
public void delStudent(int id){
SqlSession session = openSession();
try{
StudentMapper sm = session.getMapper(StudentMapper.class);
sm.delStudent(id);
session.commit();
}
finally{
session.close();
}
}
}
三、基础知识
作为一个ORM框架,可以知道其至少由对象模型转换为关系模型、关系模型转换为对象模型和缓存管理这三个模块组成。
MyBatis在对象模型转换为关系模型模块的实现方式是对象模型实例属性+自定义SQL语句,好处是对SQL语句的可操作性高,同时简化SQL入参的处理;坏处是对于简单的单表操作,依旧要写SQL语句,无法由对象模型自动生成SQL,最明显的问题是在开发初期数据表结构不稳定,一旦表结构改了,代码上不仅要改对象模型还要改SQL语句(不过MyBatis也考虑到这点,通过<sql>标签实现SQL语句复用,缓解这样问题)。
关系模型转换为对象模型则采用关系模型结果集字段映射到对象模型实体字段的方式处理。
缓存模块则分为SQL语句缓存和查询数据缓存两种,由于MyBatis需要开发者自定义SQL语句,因此SQL语句缓存不用考虑;而查询数据缓存则被分为一级和二级缓存,一级缓存以事务为作用域,二级缓存以同一个映射集为作用域,而且二级缓存采用直写的方式处理缓存数据被修改和删除的情况。
(本人不才,曾开发轻量级ORM框架LessSQL.Net,由于设计为SQL语句必须由对象模块实例映射生成,而关系模型数据集合无法自动填充任意的对象模型实体中,无法支撑复杂的查询语句,而缓存方面仅实现了SQL语句缓存性能优化有限,因此框架仅适用于小型工具软件。因为踩过这些坑,所以对ORM框架有一点浅薄的认识和看法)
言归正转,我们一起了解MyBatis的基础知识吧。
1. MyBatis框架配置文件
实际上就是MyBatis会话工厂的配置文件,用于配置如缓存、日志框架、数据库链接信息、两种模型间转换的处理器和注册映射集等。通过上文大家应该知道如何Make it work了。而Make it better也是从这里出发。
2. 映射集
映射集是由多个“标识”——“SQL语句”组成,映射记录上还有如入参类型、返回类型等信息,为对象关系模型转换引擎提供元数据。
设置映射集的方式有两种,一种是通过接口,一种通过xml文档。但上文示例采用两者相结合的方式,综合两者优点。
[a]. 映射接口方式
public interface StudentMapper{
@Select("select * from TStudentML where Name=#{0}")
EStudent getJustStudent(String name); @Insert("insert into TStudentML(Name, ClassID) values(#{name}, #{myClass.id})")
void addStudent(EStudent student); @Delete("delete from TStudentML where ID=#{0}")
void delStudent(int id); @Update("update TStudentML set Name=#{name},ClassID=#{myClass.id} where ID=#{id}")
void updateStudent(EStudent student);
}
很明显通过接口方式定义映射集是无法实现上文中复杂的查询操作,而好处就是代码量锐减,且由于使用了接口,所以IDE的智能提示和编译时语法、依赖关系检查会降低编码错误的风险。
使用接口方式需要将mybatis-config.xml中的mapper改为如下内容
<mappers>
<mapper class="com.dao.StudentMapper"></mapper>
</mappers>
<!--
或注册某包下的所有映射接口
<mappers>
<package name="com.dao"/>
</mappers>
-->
[b]. 映射XML文件
上文示例已经展示了映射XML文件的用法,下面我们逐个细节理解。
1. select、update、delete和insert标签用于填写对应动作的SQL语句。
2. parameterType属性就是入参类型具体法则如下(parameterMap已被deprecated,所以不理也罢):
a. parameterType为POJO类型时,可通过 #{属性名} 填入属性值(该属性值经过防SQL注入处理),也可通过 ${name} 填入属性raw值(未经过防SQL注入处理的属性值),更爽的是 #{} 支持短路径操作如上文中的 #{myClass.id} 。
b. parameterType为int、long等值类型时,当仅有一个入参时,可以使用 #{任意字符} 填入属性值,但无法通过 ${任意字符串} 填入属性raw值(报找不到改实例属性),还可以通过 #{} 和 #{param0} 来填入属性值;而入参为多个时,则只能使用 #{}到#{n} 和 #{param0}到#{paramn} 来填入属性值了;但由于动态SQL下的标签仅识别 #{} 等格式的占位符,因此建议通过使用 #{} 格式的占位符,保持代码一致性。
3. resultType属性就是返回值类型。
4. sql标签则用于重用SQL片段,示例如下:
<sql id="studentCols">
Name, ClassID
</sql>
<select id="qryStudent" resultType="EStudent">
select <include id="studentCols"/> from TStudentML
</select>
5. 模糊查询
网上有很多做法,但试过不是效果不好,就是报错,误打误撞发现最原始的做法 '%'+#{}+'%'就OK了!
6. 一对一关系
一对一关系MyBatis为我们提供 嵌套结果、嵌套查询 两种查询方式。由于嵌套查询需要向数据库执行两次查询操作,因此推荐使用嵌套结果方式。
嵌套结果示例:
<!-- resultMap属性值为配置返回值映射信息的resultMap标签的id值 -->
<select id="getClass" parameterType="int" resultMap="ClassResultMap">
select * from class c inner join teacher t on(c.tid = t.id) where t.id = #{0}
</select>
<!-- type属性值为返回值的JAVA数据类型 -->
<resultMap id="ClassResultMap" type="Cls">
<!--
id标签表示对象属性对应的是表主键
result标签表示对象属性对应的是普通表字段
注意:必须用id或result标出需要返回的字段/属性映射,否则在查询多条记录时,仅会返回最后一条记录
-->
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<!--
一对一关系采用association标签配置关联信息
javaType为JAVA数据类型
-->
<association property="teacher" javaType="Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap>
嵌套查询示例:
<select id="getClass" parameterType="int" resultMap="ClassResultMap">
select * from class where tid = #{id}
</select>
<select id="getTeacher" parameterType="int" resultType="Teacher">
select t_id id, t_name name from teacher where t_id = #{0}
</select>
<resultMap id="ClassResultMap" type="Cls">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<!--
select属性值为第二执行SQL语句id
而column属性值为传递给第二执行SQL语句的入参,而且入参为第一次SQL语句的查询结果集字段值
注意:若嵌套查询的条件不只一个,那么就需要将column属性设置为column="{prop1: fie;d1, prop2: field2}",然后嵌套查询的SQL中通过#{prop1},#{prop2}获取查询条件值
-->
<association prorperty="teacher" column="tid" select="getTeacher">
</association>
</resultMap>
7. 一对多关系
一对多关系同样分为 嵌套结果 和嵌套查询两种,由于嵌套查询会由于N+1次查询导致性能下降,一般推荐使用嵌套结果的做法,但有些查询操作必须使用嵌套查询才能完成。
嵌套结果示例:
<select id="getClass" parameterType="int" resultMap="ClassResultMap">
select * from class c inner join student s on(c.id = s.cid) where c.id = #{id}
</select>
<resultMap id="ClassResultMap" type="Cls">
<!--
注意:必须用id或result标出需要返回的字段/属性映射,否则在查询多条记录时,仅会返回最后一条记录
-->
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<!--
一对多关系采用collection标签来配置映射信息
ofType属性值为返回值的JAVA数据类型
-->
<collection prorperty="students" ofType="Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap>
嵌套查询示例:
<select id="getClass" parameterType="int" resultMap="ClassResultMap">
select * from class where tid = #{id}
</select>
<select id="getStudents" parameterType="int" resultType="Student">
select s_id id, s_name name from student where cid = #{id}
</select>
<resultMap id="ClassResultMap" type="Cls">
<!-- 注意:若不写这句,那么c_id字段将作为嵌套查询的条件,而不会赋值到id属性了 -->
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<collection prorperty="students" column="c_id" select="getStudents">
</collection>
</resultMap>
8. 动态SQL
MyBatis的动态SQL与数据库中通过exec、sp_executesql()等执行的动态SQL目的是一致的,只是操作形式的不同而已。MyBatis的动态SQL定义方式上与文本模板定义无异,定义后均为经过类似于模板引擎的模块进行解析得到最终的数据。下面我们来了解具体的标签吧。
[a]. <if test="逻辑条件判断"></if>
如果test内返回true,则标签体的内容将被添加到最终结果中。示例:
<if test="name != null and job != null">
and Name like '%'+#{name}+'%' and Description like '%'+#{job}+'%'
</if>
注意:test语句中的逻辑条件判断必须使用入参的属性名或键名,而不能使用#{0}或#{1}等形式的入参,否则条件判断一律视为true。
[b]. <choose></choose>
相当于Java的switch语句。示例:
<choose>
<when test="#{title} !=null">
and Title = #{title}
</when>
<when test="#{name} !=null">
and Name = #{name}
</when>
<otherwise>
and Age >
</otherwise>
</choose>
[c]. <where></where>
用于处理动态条件时,where留存与否的尴尬。具体就是
select * from tbl
where
<if test="#{name}!=null">
Name = #{name}
</if>
<if test="#{title}!=null">
and Title = #{title}
</if>
当两个条件都不符合时,sql语句就变成 select * from tbl where ,报错是必然的。而 where标签 会根据其标签体是否有值来决定是否插入where关键字,并会自动去除无用的 or 和 and 关键字。示例:
<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>
[d]. <set></set>
用于在update语句中,动态设置更新的列。示例:
<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>
[e].<trim></trim>
当标签体有内容时则为内容添加前缀、后缀,而且可以除去内容前后部分内容。与 where标签 功能相同的示例:
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<!--
prefix属性值为添加到内容的前缀信息
prefixOverrides属性值为除去内容前的内容,当需要除去多个内容时,使用管道符|分割,注意:空格也将被除去
-->
<trim prefix="where" prefixOverrides="AND |OR ">
<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>
</trim>
</select>
与 set标签 功能相同的示例:
<update id="updateAuthorIfNecessary">
update Author
<trim prefix="set" suffixOverrides=",">
<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>
</trim>
where id=#{id}
</update>
[f]. <foreach></foreach>
主要用于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>
入参为List或Array类型时,MyBatis会自动将其添加到内部Map对象中,并且List类型对象的键名为list,Array类型对象的键名为array,并通过 foreach标签 的collection属性指定入参的键名。而item属性用于指定 foreach标签 内集合元素的占位符名称,index属性则指定 foreach标签 内当前元素索引的占位符名称,而open、close和separator属性则分别指定动态SQL的开头、结束文本和集合元素项间的分隔符。
3. 操作数据
如上文示例那样,通过 SqlSessionFactoryBuilder实例 生成 SqlSessionFactory实例 ,然后在生成操作数据库的 SqlSession实例 。
需要注意的是:
a. 每次使用完 SqlSession实例 必须调用其 close() 方法释放链接;
b. 由于mybatis-config.xml中 <transactionManager type="JDBC"></transactionManager> 而且通过 SqlSessionFactory实例.openSession() 获取链接对象,因此链接对象默认时不会自动提交增、删和改操作的,因此需要调用 commit() 方法手动提交操作。
4. 生命周期
[a]. SqlSessionFactoryBuilder
由于`SqlSessionFactoryBuilder`实例用于生成`SqlSessionFactory`实例而已,因此并没有必要以应用程序全局作为作用域,并且无必要多线程共享。因此作为函数的局 部变量使用即可。
[b]. SqlSessionFactory
作为数据库连接池和连接池管理器使用,为达到数据库连接复用的效果,`SqlSessionFactory`实例应当以程序全局作为作用域,并且多线程共享。采用单例或静态单例模式较好
[c]. SqlSession
由于`SqlSession`实例非线程安全,因此作为函数的局部变量使用。而且由于数据库连接为共享资源,因此必须遵循晚调用早释放原则, 确保调用`close()`函数释放连接。
四、总结
初尝MyBatis时会觉得麻烦,尤其是使用过Hibernate或其他可将对象模型实例自动转成SQL语句的框架的朋友来说,这确实太不方便了,而且容易出错。其实我们可以将很多工作交给相关的工具去解决。以后慢慢说吧!
尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/4014819.html ^_^肥仔John
MyBatis魔法堂:即学即用篇的更多相关文章
- MyBatis魔法堂:ResultMap详解
一.前言 MyBatis是基于“数据库结构不可控”的思想建立的,也就是我们希望数据库遵循第三范式或BCNF,但实际事与愿违,那么结果集映射就是MyBatis为我们提供这种理想与现实间转换的手段了, ...
- MyBatis魔法堂:各数据库的批量Update操作
一.前言 MyBatis的update元素的用法与insert元素基本相同,因此本篇不打算重复了.本篇仅记录批量update操作的sql语句,懂得SQL语句,那么MyBatis部分的操作就简单了. ...
- MyBatis魔法堂:Insert操作详解(返回主键、批量插入)
一.前言 数据库操作怎能少了INSERT操作呢?下面记录MyBatis关于INSERT操作的笔记,以便日后查阅. 二. insert元素 属性详解 其属性如下: parameterType ...
- MyBatis魔法堂:Insert操作详解
一.前言 数据库操作怎能少了INSERT操作呢?下面记录MyBatis关于INSERT操作的笔记,以便日后查阅. 二. insert元素 属性详解 其属性如下: parameterType:入参的全限 ...
- JS魔法堂:不完全国际化&本地化手册 之 理論篇
前言 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...
- JS魔法堂:不完全国际化&本地化手册 之 实战篇
前言 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...
- .Net魔法堂:史上最全的ActiveX开发教程——ActiveX与JS间交互篇
一.前言 经过上几篇的学习,现在我们已经掌握了ActiveX的整个开发过程,但要发挥ActiveX的真正威力,必须依靠JS.下面一起来学习吧! 二.JS调用ActiveX方法 只需在UserContr ...
- .Net魔法堂:史上最全的ActiveX开发教程——自动更新、卸载篇
一.前言 B/S模式的特点之一,客户端版本升级相对简单.快捷,适合产品的快速迭代.而ActiveX组件的自动更新同样也继承了这一优点.下面我们一起来了解吧! 二.二话不说更新ActiveX 1. 设置 ...
- .Net魔法堂:史上最全的ActiveX开发教程——部署篇
一.前言 接<.Net魔法堂:史上最全的ActiveX开发教程——发布篇>,后我们继续来部署吧! 二. 挽起衣袖来部署 ActiveX的部署其实就是客户端安装ActiveX组件,对未签 ...
随机推荐
- 下载老版本的Xcode
1.苹果开发者中心,找到Xcode 2.点击下载 3,找到Support 4.找到所需的版本,点击"+"下载 5.安装Xcode,愉快的开发.
- 跟着百度学PHP[4]-OOP面对对象编程-2-属性和方法
简单的说 变量就是成员属性函数就是成员方法(方法有三:构造方法[即为__construct].成员方法.析构方法[__destruct]) 成员方法和成员属性都是可以加修饰词.比如封装性的方法或者属性 ...
- Android 趣味应用—— 短信编辑器
修改短信数据库,从而生成任意手机号发送的短信. AndroidManifest.xml <?xml version="1.0" encoding="utf-8&qu ...
- POJ 1273 网络流(最大流)模板
http://poj.org/problem?id=1273 这道题很值得反思,弄了一下午,交上去先是一直编译错误,而在本地运行没有问题, 原因可能是oj的编译器版本老旧不支持这样的写法 G[from ...
- fastx_toolkit软件使用说明
高通量测序数据下机后的原始fastq文件,包含4行,其中一行为质量值,另外一行则为对应序列,我们都了解高通量的数据处理首先要进行质量控制,这些过程包括去接头.过滤低质量reads.去除低质量的3'和5 ...
- 18.1---不用加号的加法(CC150)
1,自己写的又长又臭的代码,也能AC,但是太丑了.主要是通过二进制来算. public static int addAB(int a, int b){ int res = 0; String str1 ...
- 开源中国git使用方法
1.添加公匙. 打开开源中国git公匙管理 用TortoiseGit Puttygen生成公匙文件 生成过程中 不断移动点击鼠标(因为生成密匙过程是记录鼠标变化作为加密过程) ...
- 应用HTK搭建语音拨号系统1:数据准备
选自:http://maotong.blog.hexun.com/6204849_d.html 应用HTK搭建语音拨号系统--数据准备 苏统华 哈尔滨工业大学人工智能研究室 2006年10月30日 声 ...
- Java语言中几个常用的包
Java采用包结构来组织和管理类和接口文件.本文介绍Java语言类库中几个常用的包,因为这几个包在软件开发与应用中经常需要用到,其中有些包是必要的.若是离开它,还真不能做事情了. 第一个包:java. ...
- 【GoLang】GoLang 单元测试、性能测试使用方法
单元测试代码: ackage test import ( // "fmt" "testing" ) func Test_FlowControl(t *testi ...