增删改查实现

在实际使用中,MyBatis 的使用遵从一定的规范。

常用的增删改查的 MyBatis 实现如下:

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="cn.sail.mapper.UserMapper"> <resultMap id="resultMap" type="user">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="pwd"/>
</resultMap> <sql id="selectAll">
select id,
name,
pwd
from user
</sql> <select id="selectList" parameterType="user" resultMap="resultMap">
<include refid="selectAll"/>
<where>
<if test="name != null and name != ''">
and name like concat('%', #{name}, '%')
</if>
<if test="pwd != null and pwd != ''">
and pwd like concat('%', #{pwd}, '%')
</if>
</where>
limit #{startIndex}, #{pageSize}
</select> <select id="pageByRowBounds" parameterType="user" resultMap="resultMap">
<include refid="selectAll"/>
<where>
<if test="name != null and name != ''">
and name like concat('%', #{name}, '%')
</if>
<if test="pwd != null and pwd != ''">
and pwd like concat('%', #{pwd}, '%')
</if>
</where>
</select> <select id="selectOne" parameterType="int" resultMap="resultMap">
<include refid="selectAll"/>
where id = #{id}
</select> <insert id="insert" parameterType="user">
insert into user
<trim prefix="(" suffixOverrides="," suffix=")">
<if test="name != null">name,</if>
<if test="pwd != null">pwd,</if>
</trim>
<trim prefix="values (" suffixOverrides="," suffix=")">
<if test="name != null">#{name},</if>
<if test="pwd != null">#{pwd},</if>
</trim>
</insert> <insert id="insertBatch" parameterType="list">
insert into user
(name, pwd)
values
<foreach item="user" collection="list" separator=",">
(#{user.name}, #{user.pwd})
</foreach>
</insert> <update id="update" parameterType="user">
update user
<trim prefix="set" suffixOverrides=",">
<if test="name != null">name = #{name},</if>
<if test="pwd != null">pwd = #{pwd},</if>
</trim>
where id = #{id}
</update> <update id="updateBatch1" parameterType="list">
insert into user
(id, name, pwd)
values
<foreach item="user" collection="list" separator=",">
(#{user.id}, #{user.name}, #{user.pwd})
</foreach>
on duplicate key update
name = values(name), pwd = values(pwd)
</update> <update id="updateBatch2" parameterType="list">
replace into user
(id, name, pwd)
values
<foreach item="user" collection="list" separator=",">
(#{user.id}, #{user.name}, #{user.pwd})
</foreach>
</update> <delete id="delete" parameterType="int">
delete from user
where id = #{id}
</delete> <delete id="deleteBatch" parameterType="string">
delete from user
where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete> </mapper>

Mapper.java

public interface UserMapper {

    /**
* 列表
* @return 用户列表
* @param user 用户
*/
List<User> selectList(User user); /**
* 通过RowBounds分页
* @return 用户列表
* @param user 用户
*/
List<User> pageByRowBounds(User user); /**
* 单项
* @param id 主键
* @return 用户
*/
User selectOne(int id); /**
* 插入
* @param user 用户
* @return 插入数量
*/
int insert(User user); /**
* 批量插入
* @param userList 用户列表
* @return 插入数量
*/
int insertBatch(List<User> userList); /**
* 更新
* @param user 用户
* @return 更新数量
*/
int update(User user); /**
* 批量更新1
* @param userList 用户列表
* @return 更新数量
*/
int updateBatch1(List<User> userList); /**
* 批量更新2
* @param userList 用户列表
* @return 更新数量
*/
int updateBatch2(List<User> userList); /**
* 删除
* @param id 主键
* @return 删除数量
*/
int delete(int id); /**
* 批量删除
* @param ids 主键数组
* @return 删除数量
*/
int deleteBatch(int[] ids); }

Test.java

public class MyBatis {

    /**
* 查询列表
*/
@Test
public void selectList() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
int pageNum = 1;
int pageSize = 2;
user.setStartIndex((pageNum - 1) * pageSize);
user.setPageSize(pageSize);
List<User> userList = userMapper.selectList(user);
System.out.println(userList);
sqlSession.close();
} /**
* RowBounds分页
*/
@Test
public void pageByRowBounds() {
SqlSession sqlSession = MybatisUtils.getSession();
User user = new User();
int pageNum = 1;
int pageSize = 2;
RowBounds rowBounds = new RowBounds((pageNum - 1) * pageSize, pageSize);
List<User> userList = sqlSession.selectList("cn.sail.mapper.UserMapper.pageByRowBounds", null, rowBounds);
System.out.println(userList);
sqlSession.close();
} /**
* 查询单项
*/
@Test
public void selectOne() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectOne(1);
System.out.println(user);
sqlSession.close();
} /**
* 插入
*/
@Test
public void insert() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setName("sail");
user.setPwd("123456");
int insert = userMapper.insert(user);
System.out.println(insert);
sqlSession.commit();
sqlSession.close();
} /**
* 批量插入
*/
@Test
public void insertBatch() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = new ArrayList<>();
User user = new User();
user.setName("sail1");
user.setPwd("123456");
userList.add(user);
user = new User();
user.setName("sail2");
user.setPwd("123456");
userList.add(user);
int insertBatch = userMapper.insertBatch(userList);
System.out.println(insertBatch);
sqlSession.commit();
sqlSession.close();
} /**
* 更新
*/
@Test
public void update() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(6);
user.setName("sail");
user.setPwd("12345678");
int update = userMapper.update(user);
System.out.println(update);
sqlSession.commit();
sqlSession.close();
} /**
* 批量更新1
*/
@Test
public void updateBatch1() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = new ArrayList<>();
User user = new User();
user.setId(11);
user.setName("sail1");
user.setPwd("1234567");
userList.add(user);
user = new User();
user.setId(12);
user.setName("sail2");
user.setPwd("1234567");
userList.add(user);
int updateBatch1 = userMapper.updateBatch1(userList);
System.out.println(updateBatch1);
sqlSession.commit();
sqlSession.close();
} /**
* 批量更新2
*/
@Test
public void updateBatch2() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = new ArrayList<>();
User user = new User();
user.setId(11);
user.setName("sail1");
user.setPwd("12345678");
userList.add(user);
user = new User();
user.setId(12);
user.setName("sail2");
user.setPwd("12345678");
userList.add(user);
int updateBatch2 = userMapper.updateBatch2(userList);
System.out.println(updateBatch2);
sqlSession.commit();
sqlSession.close();
} /**
* 删除
*/
@Test
public void delete() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int delete = userMapper.delete(6);
System.out.println(delete);
sqlSession.commit();
sqlSession.close();
} /**
* 批量删除
*/
@Test
public void deleteBatch() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int[] ids = {9, 10};
int deleteBatch = userMapper.deleteBatch(ids);
System.out.println(deleteBatch);
sqlSession.commit();
sqlSession.close();
} }

resultMap标签

<resultMap id="resultMap" type="user">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="pwd"/>
</resultMap>

resultMap 中,column 是数据库表的列名 , property 是对应实体类的属性名。

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以省略大量的赋值代码,并可以灵活的给字段取别名,且可以复用。

resultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。

sql片段

<sql id="selectAll">
select id,
name,
pwd
from user
</sql>

sql 片段一般编写通用性的 SQL 语句,比如全字段查询,过滤条件封装等。

最好基于单表来定义 sql 片段,提高片段的可重用性。

在 sql 片段中不要包括 where。

sql 片段是需要用 include 标签引用的,如以上的 sql 片段可以用如下的 include 标签引用:

<include refid="selectAll"/>

引用 sql 片段时,如果 refid 指定的不在本文件中,那么需要在前面加上 namespace。

列表分页查询

<select id="selectList" parameterType="user" resultMap="resultMap">
<include refid="selectAll"/>
<where>
<if test="name != null and name != ''">
and name like concat('%', #{name}, '%')
</if>
<if test="pwd != null and pwd != ''">
and pwd like concat('%', #{pwd}, '%')
</if>
</where>
limit #{startIndex}, #{pageSize}
</select>
/**
* 列表
* @return 用户列表
* @param user 用户
*/
List<User> selectList(User user);
/**
* 查询列表
*/
@Test
public void selectList() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
int pageNum = 1;
int pageSize = 2;
user.setStartIndex((pageNum - 1) * pageSize);
user.setPageSize(pageSize);
List<User> userList = userMapper.selectList(user);
System.out.println(userList);
sqlSession.close();
}

列表查询往往是一个页面需要编写的第一个 SQL,也往往是数据量最大、语句最复杂的 SQL。

parameterType 这里使用的是配置文件中取的别名,对应实体类。

resultMap 这里使用的是前面定义的 resultMap。

如果用 resultType ,则需要指定具体的类或者 MyBatis 默认的基本数据类型。

MyBatis 默认的基本数据类型有:int、string、long、map。

where 标签会知道如果它包含的标签中有返回值的话,它就插入一个 where 。此外,如果标签返回的内容是以 and 或 or 开头的,则它会剔除掉。

if 标签用于判断参数是否有值,有值则拼接标签中的 SQL 语句,没有值则不拼接,可以提高 SQL 查询效率和避免传值为 null 的语法错误。

#{} 用于传递参数,会默认在首尾拼接 ‘ 。

${} 用于传递直接量,即不会在首尾拼接 ’ ,常用于拼接 SQL 语句。

limit 处没有进行参数有值判断,所以 startIndex 和 pageSize 必须有值,不然语句会报错。

以上是常用的分页查询处理方式,也可以用 Java 代码实现分页,如RowBounds分页:

<select id="pageByRowBounds" parameterType="user" resultMap="resultMap">
<include refid="selectAll"/>
<where>
<if test="name != null and name != ''">
and name like concat('%', #{name}, '%')
</if>
<if test="pwd != null and pwd != ''">
and pwd like concat('%', #{pwd}, '%')
</if>
</where>
</select>
/**
* 通过RowBounds分页
* @return 用户列表
* @param user 用户
*/
List<User> pageByRowBounds(User user);
@Test
public void pageByRowBounds() {
SqlSession sqlSession = MybatisUtils.getSession();
User user = new User();
int pageNum = 1;
int pageSize = 2;
RowBounds rowBounds = new RowBounds((pageNum - 1) * pageSize, pageSize);
List<User> userList = sqlSession.selectList("cn.sail.mapper.UserMapper.pageByRowBounds", null, rowBounds);
System.out.println(userList);
sqlSession.close();
}

此种分页方式的代码较为繁琐,且由于进行了封装,效率也不如上面的方式,不推荐使用。

查询单项

<select id="selectOne" parameterType="int" resultMap="resultMap">
<include refid="selectAll"/>
where id = #{id}
</select>
/**
* 单项
* @param id 主键
* @return 用户
*/
User selectOne(int id);
/**
* 查询单项
*/
@Test
public void selectOne() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectOne(1);
System.out.println(user);
sqlSession.close();
}

查询单项常用于详情查看和修改时赋值。

由于主键项是没有做是否有值判断的,因此必须有值,是否会造成语法错误。

由于单项查询的返回值往往是一个类,因此要注意非空判断,避免空指针异常。

插入

<insert id="insert" parameterType="user">
insert into user
<trim prefix="(" suffixOverrides="," suffix=")">
<if test="name != null">name,</if>
<if test="pwd != null">pwd,</if>
</trim>
<trim prefix="values (" suffixOverrides="," suffix=")">
<if test="name != null">#{name},</if>
<if test="pwd != null">#{pwd},</if>
</trim>
</insert>
/**
* 插入
* @param user 用户
* @return 插入数量
*/
int insert(User user);
@Test
public void insert() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setName("sail");
user.setPwd("123456");
int insert = userMapper.insert(user);
System.out.println(insert);
sqlSession.commit();
sqlSession.close();
}

插入涉及到数据变更,在实际使用中建议加上 @Transactional(rollbackFor = Exception.class)注解,在出错时会进行回滚,避免造成数据错误。

主键值往往是自增的,插入时一般不需要设置主键值。

trim 标签可以用 suffixOverrides 属性过滤掉 SQL 语句尾部多余的符号,也可以用 prefix 拼接标签中开头的语句,用 suffix 标签拼接标签中结尾的语句。

批量插入

<insert id="insertBatch" parameterType="list">
insert into user
(name, pwd)
values
<foreach item="user" collection="list" separator=",">
(#{user.name}, #{user.pwd})
</foreach>
</insert>
/**
* 批量插入
* @param userList 用户列表
* @return 插入数量
*/
int insertBatch(List<User> userList);
@Test
public void insertBatch() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = new ArrayList<>();
User user = new User();
user.setName("sail1");
user.setPwd("123456");
userList.add(user);
user = new User();
user.setName("sail2");
user.setPwd("123456");
userList.add(user);
int insertBatch = userMapper.insertBatch(userList);
System.out.println(insertBatch);
sqlSession.commit();
sqlSession.close();
}

foreach标签

  • collection:指定输入对象中的集合属性。
  • item:每次遍历生成的对象。
  • open:开始遍历时的拼接字符串。
  • close:结束时拼接的字符串。
  • separator:遍历对象之间需要拼接的字符串。

修改

<update id="update" parameterType="user">
update user
<trim prefix="set" suffixOverrides=",">
<if test="name != null">name = #{name},</if>
<if test="pwd != null">pwd = #{pwd},</if>
</trim>
where id = #{id}
</update>
/**
* 更新
* @param user 用户
* @return 更新数量
*/
int update(User user);
@Test
public void update() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(6);
user.setName("sail");
user.setPwd("12345678");
int update = userMapper.update(user);
System.out.println(update);
sqlSession.commit();
sqlSession.close();
}

参数中主键的值是必须的。

更新涉及到数据变更,在实际使用中建议加上 @Transactional(rollbackFor = Exception.class)注解,在出错时会进行回滚,避免造成数据错误。

由于一般会进行修改字段的非空判断,所以当一个字段有值改为无值时,由于无值的参数被非空判断拦截,是修改不成功的,如果有此需求则需要单独定义没有字段非空判断的 SQL 语句。

批量更新1

<update id="updateBatch1" parameterType="list">
insert into user
(id, name, pwd)
values
<foreach item="user" collection="list" separator=",">
(#{user.id}, #{user.name}, #{user.pwd})
</foreach>
on duplicate key update
name = values(name), pwd = values(pwd)
</update>
/**
* 批量更新1
* @param userList 用户列表
* @return 更新数量
*/
int updateBatch1(List<User> userList);
@Test
public void updateBatch1() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = new ArrayList<>();
User user = new User();
user.setId(11);
user.setName("sail1");
user.setPwd("1234567");
userList.add(user);
user = new User();
user.setId(12);
user.setName("sail2");
user.setPwd("1234567");
userList.add(user);
int updateBatch1 = userMapper.updateBatch1(userList);
System.out.println(updateBatch1);
sqlSession.commit();
sqlSession.close();
}

on duplicate key update,是基于主键(PRIMARY KEY)或唯一索引(UNIQUE INDEX)使用的。

如果已存在该唯一标示或主键就更新,如果不存在该唯一标示或主键则作为新行插入。

批量更新2

<update id="updateBatch2" parameterType="list">
replace into user
(id, name, pwd)
values
<foreach item="user" collection="list" separator=",">
(#{user.id}, #{user.name}, #{user.pwd})
</foreach>
</update>
/**
* 批量更新2
* @param userList 用户列表
* @return 更新数量
*/
int updateBatch2(List<User> userList);
@Test
public void updateBatch2() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = new ArrayList<>();
User user = new User();
user.setId(11);
user.setName("sail1");
user.setPwd("12345678");
userList.add(user);
user = new User();
user.setId(12);
user.setName("sail2");
user.setPwd("12345678");
userList.add(user);
int updateBatch2 = userMapper.updateBatch2(userList);
System.out.println(updateBatch2);
sqlSession.commit();
sqlSession.close();
}

replace into 跟 insert into 的用法完全一样,但是它带有更新功能。

如果发现表中已经有此行数据(根据主键或者唯一索引判断)则先删除此行数据,然后插入新的数据。否则,直接插入新数据。

它是先删除数据,然后再插入,如果当前的数据库用户没有删除权限,是不能使用replace into的。

删除

<delete id="delete" parameterType="int">
delete from user
where id = #{id}
</delete>
/**
* 删除
* @param id 主键
* @return 删除数量
*/
int delete(int id);
@Test
public void delete() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int delete = userMapper.delete(6);
System.out.println(delete);
sqlSession.commit();
sqlSession.close();
}

参数中主键是必须的。

删除涉及到数据变更,在实际使用中建议加上 @Transactional(rollbackFor = Exception.class)注解,在出错时会进行回滚,避免造成数据错误。

批量删除

<delete id="deleteBatch" parameterType="string">
delete from user
where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
/**
* 批量删除
* @param ids 主键数组
* @return 删除数量
*/
int deleteBatch(int[] ids);
@Test
public void deleteBatch() {
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int[] ids = {9, 10};
int deleteBatch = userMapper.deleteBatch(ids);
System.out.println(deleteBatch);
sqlSession.commit();
sqlSession.close();
}

以上就是常用的增删改查语句,在实际使用中,为效率考虑,建议尽量使用单表进行增删改查,那些复杂的关联在代码中处理。

补充

@Param

@Param 注解用于给方法参数起一个名字。以下是总结的使用原则:

  • 在方法只接受一个参数的情况下,可以不使用 @Param。
  • 在方法接受多个参数的情况下,建议一定要使用 @Param 注解给参数命名。
  • 如果参数是 JavaBean, 则不能使用 @Param。
  • 不使用 @Param 注解时,参数只能有一个,并且是 Javabean。

注解写SQL

mybatis 最初配置信息是基于 XML,映射语句(SQL)也是定义在 XML 中的。而到 MyBatis 3 提供了新的基于注解的配置。

注解主要分成 :

  • @select ()
  • @update ()
  • @Insert ()
  • @delete ()

可以在注解中编写 SQL 语句实现与 XML 同样的效果。

利用注解开发就不需要mapper.xml映射文件了。

Java 注解的的表达力和灵活性十分有限,稍微复杂一点的 SQL 语句用注解进行编写会非常困难,因此不建议使用。

多对一和一对多处理

多对一

多对一的理解:

  • 多个学生对应一个老师。
  • 如果对于学生这边,就是一个多对一的现象,即从学生这边关联一个老师。

按查询嵌套处理

<select id="getStudents" resultMap="StudentTeacher">
select *
from student
</select> <resultMap id="StudentTeacher" type="Student">
<!-- association关联属性:
property属性名
column在多的一方的表中的列名
column="{key=value,key=value}"
其实就是键值对的形式,key是传给下个sql的取值名称,value是上个sql查询的字段名。
javaType属性类型
select引用查询结果 -->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap> <select id="getTeacher" resultType="teacher">
select *
from teacher
where id = #{id}
</select>

按结果嵌套处理

<select id="getStudents2" resultMap="StudentTeacher2" >
select s.id sid,
s.name sname,
t.name tname
from student s, teacher t
where s.tid = t.id
</select> <resultMap id="StudentTeacher2" type="Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<!--关联对象property 关联对象在Student实体类中的属性-->
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>

按照查询进行嵌套处理就像SQL中的子查询

按照结果进行嵌套处理就像SQL中的联表查询

多对一的情况用 XML 处理较为复杂,建议在代码中进行处理,逻辑会清晰很多,由于不会关联表,查询效率也会大大提高。

一对多

一对多的理解:

  • 一个老师拥有多个学生。
  • 如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)。

按查询嵌套处理

<select id="getTeacher2" resultMap="TeacherStudent2">
select *
from teacher
where id = #{id}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<!--column是一对多的外键 , 写的是一的主键的列名-->
<collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select *
from student
where tid = #{id}
</select>

按结果嵌套处理

<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,
s.name sname,
t.name tname,
t.id tid
from student s,teacher t
where s.tid = t.id
and t.id = #{id}
</select> <resultMap id="TeacherStudent" type="Teacher">
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid" />
<result property="name" column="sname" />
<result property="tid" column="tid" />
</collection>
</resultMap>

总结

association:关联

collection:集合

association 是用于一对一和多对一,而collection是用于一对多的关系

JavaType和ofType都是用来指定对象类型的

JavaType 是用来指定pojo中属性的类型

ofType 指定的是映射到list集合属性中pojo的类型。

choose语句

有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,它类似于 if-else 语句。

<select id="queryBlogChoose" parameterType="map" resultType="blog">
select *
from blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>

最新文章

  1. 【基于WPF+OneNote+Oracle的中文图片识别系统阶段总结】之篇二:基于OneNote难点突破和批量识别
  2. Skyshop: Image-Based Lighting Tools &amp; Shaders插件调整反射光不明显的模型
  3. Centos7的firewalld配置
  4. js事件代理(委托)
  5. WP8_(windows phone环境下)上传文件从C#到php接口
  6. UINavigationController与UITabbarController的样式
  7. mysql中 case when的使用
  8. 【spoj SEQN】【hdu 3439】Sequence
  9. Cordic 算法之 反正切
  10. LNA
  11. CSharpGL(47)你好,Framebuffer!
  12. 基于规则评分的密码强度检测算法分析及实现(JavaScript)
  13. python timeit模块用法
  14. Centos 安装dhcp及简单配置
  15. poj2385 Apple Catching(dp状态转移方程推导)
  16. Ubuntu下codeblocks不能自动缩进的问题
  17. gleez框架获得时间控件
  18. linux 安装crontab执行定时任务
  19. 《Linux内核分析》第四周学习总结 扒开系统调用的三成皮(上)
  20. poj做的题

热门文章

  1. HTML5续集
  2. 换个角度带你学C语言的基本数据类型
  3. Mesokurtic,Leptokurtic, Platykurtic介绍
  4. 树莓派使用Docker部署EdgeX(jakarta版本)
  5. STC8H开发(十一): GPIO单线驱动多个DS18B20数字温度计
  6. 基于数传电台的组态王控制实现远程采集控制器PLC
  7. Docker容器Nginx负载均衡配置、check及stub模块安装
  8. 开源流程引擎camunda如何扩展
  9. 开发工具-Typora编辑器下载地址
  10. ShardingSphere-proxy-5.0.0分布式雪花ID生成(三)