java线程同步之callable与future

  • 背景:

    多线程编程中,若使用Thread或者Runnable则无法便捷地获取线程的执行结果;此外在线程同步时,处理超时任务时可以省得自己创建守护线程,所以应该使用CallableFuture,(Callable和Future的基本描述)这样也能方便地配合ExecutorService使用。

  • 基本用法:

    1
    2
    3
    4
    5
    6
    7
    Future<String> future=DbThreadPool.INSTANCE.add(daemonThread);
    //1秒内没得到结果就不再等待:
    future.get(1000, TimeUnit.MILLISECONDS);
    /*取消Callable任务,不管有没在执行:*/
    future.cancel(true);
    /**假如还没在执行,取消Callable任务:*/
    future.cancel(false);

这样线程同步就可以用语言特性方便地解决了。然而实际项目使用tesseract时,调用的是linux命令,所以创建的是进程,并没有在jdk中找到直接的语言特性支持来解决子进程的超时问题,还是得手动创建守护线程解决进程和线程的同步问题。

其实也可以使用tess4J来调用tesseract,避免自己创建进程,但是经过测试,tess4J的效率低得惊人,而且包比较大,所以放弃使用它。

完整的代码:

  1. 首先创建线程池:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    public enum DbThreadPool {
    INSTANCE{
    int cpuNums = Runtime.getRuntime().availableProcessors();
    int poolSize = 10;
    int numThreads = cpuNums * poolSize;
    ExecutorService executorService = new ThreadPoolExecutor(numThreads, numThreads, 0L,
    TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

    @Override
    public <M> Future<M> add(Callable<M> task) {
    return executorService.submit(task);
    }
    @Override
    public void add(Runnable task) {
    executorService.execute(task);
    }
    @Override
    protected void shutdown() {
    //等待任务结束后关闭线程池.
    executorService.shutdown();
    }
    @Override
    protected List<Runnable> shutdownNow() {
    //不等待任务结束,直接关闭线程池.
    return executorService.shutdownNow();
    }

    };
    protected abstract <M> Future<M> add(Callable<M> task);

    protected abstract void add(Runnable task);
    protected abstract void shutdown();
    protected abstract List<Runnable> shutdownNow();
    }
  2. 守护线程:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class DaemonThread implements Callable<String> {
    private Process process;
    public DaemonThread(Process process) {
    this.process = process;
    }
    @Override
    public String call() throws Exception {
    try {
    System.out.println("睡了!");
    Thread.sleep(500); // 延迟0.5秒
    System.out.println("醒了!");
    } catch (InterruptedException e) {
    System.out.println(e.getMessage());
    }
    System.out.println("操作前");
    process.destroy();
    System.out.println("操作后");
    return "Success";
    }
    }
  3. 脚本调用类,其实这个也没必要用单例,只是自己想多写几次练熟一点:
    synchronized也不是必要的,只是实际使用时希望控制创建的子进程数量, 不希望创建太多子进程,而是维持在1个子进程。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import java.util.concurrent.Future;

    public enum CmdInvokeSingleton {
    INSTANCE {
    /**
    * 运行shell脚本
    * @param shell 需要运行的shell脚本
    */
    @Override
    synchronized protected void execShell(String shell) {
    try {
    Runtime rt = Runtime.getRuntime();
    final Process process = rt.exec(shell);
    DaemonThread daemonThread = new DaemonThread(process);
    Future<String> future=DbThreadPool.INSTANCE.add(daemonThread);
    int exitcode = process.waitFor();
    System.out.println("exitcode:" + exitcode);
    future.cancel(true);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    };
    protected abstract void execShell(String shell);
    }

git回退版本

  • 背景:
    当前版本2.0, 试图把版本回退到一个已经提交(commit)的版本。
  • 原理:
    1. 要从远端版本树上检出(check out)某版本(比如1.0)到本地;
    2. 把本地库推送到远端,选择强制覆盖即可。
      (注意到2.0从此就丢失了。)

如果使用tortoisegit :

  1. 项目目录内右键选择tortoisegit->显示日志
  2. 在版本树上选择自己想要回退到的版本,右键选择重置devel到这个版本
  3. 项目目录内右键选择tortoisegit->推送,勾选强制覆盖已存在的分支

Singleton 单例模式

网上说得很清楚了:
Singleton链接
CmdInvokeSingleton.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public enum CmdInvokeSingleton {
INSTANCE {
/**
* 运行shell脚本
*
* @param shell 需要运行的shell脚本
*/
@Override
protected void execShell(String shell) {
try {
Runtime rt = Runtime.getRuntime();
final Process process = rt.exec(shell);
DaemonThread daemonThread = new DaemonThread(process);
daemonThread.start();//Destroy process if necessary
int exitcode = process.waitFor();
daemonThread.interrupt();
System.out.println("finish:" + exitcode);
} catch (Exception e) {
e.printStackTrace();
}
}

};
protected abstract void execShell(String shell);
}

DaemonThread.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class DaemonThread extends Thread {
/**
* 监视指定的进程,3秒后强制销毁它。
* */
private Process process;
public DaemonThread(Process process) {
this.process = process;
}
public void run() {
try {
sleep(3000); // 延迟3秒
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
process.destroy();
}
}

调用的时候:

1
2
3
4
5
6
7
try {
String cmdString = "ls > out.txt";
CmdInvokeSingleton.INSTANCE.execShell(cmdString);
} catch (Exception e) {
e.printStackTrace();
return "invoke failed.";
}

下一步可以学习cakephp。

Gson与FileUtil/IOUtil笔记

  1. 直接从代码学习:
    Gson三种基本用法,
    1
    2
    3
    Gson gson=new Gson();
    Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(Foo.class, new FooAdapter()).create();
    Gson gson =new GsonBuilder().registerTypeAdapterFactory(FooAdapterFactory.INSTANCE).create();
    完整测试代码如下:(其中TypeAdapterFactory的测试来自Stackoverflow,作者还用上了单例)
    Foo.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    package testGson;

    import java.io.Serializable;

    public class Foo implements Serializable {
    private static final long serialVersionUID = -1455994233292753460L;

    public Foo() {
    setDataString("data");
    setIdInteger(100);
    setNameString("name");
    setValueString("value");
    }

    public String getNameString() {
    return nameString;
    }

    public void setNameString(String nameString) {
    this.nameString = nameString;
    }

    public String getValueString() {
    return valueString;
    }

    public void setValueString(String valueString) {
    this.valueString = valueString;
    }

    public Integer getIdInteger() {
    return idInteger;
    }

    public void setIdInteger(Integer idInteger) {
    this.idInteger = idInteger;
    }

    public String getDataString() {
    return dataString;
    }

    public void setDataString(String dataString) {
    this.dataString = dataString;
    }

    String nameString;
    String valueString;
    Integer idInteger;
    String dataString;
    }

FooAdapter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package testGson;

import java.io.IOException;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

public class FooAdapter extends TypeAdapter<Foo>{

@Override
public void write(JsonWriter out, Foo obj) throws IOException {
try {
out.beginObject();
out.name("idIntegerFromAdapter").value(obj.getIdInteger());
out.name("nameStringFromAdapter").value(obj.getNameString());
out.name("valueStringFromAdapter").value(obj.getValueString());
out.name("dataStringFromAdapter").value(obj.getDataString());
out.endObject();
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public Foo read(JsonReader in) throws IOException {
final Foo obj = new Foo();
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "idIntegerFromAdapter":
obj.setIdInteger(in.nextInt());
break;
case "nameStringFromAdapter":
obj.setNameString(in.nextString());
break;
case "valueStringFromAdapter":
obj.setValueString(in.nextString());
break;
case "dataStringFromAdapter":
obj.setDataString(in.nextString());
break;
default:
break;

}
}
in.endObject();
return obj;
}
}

FooAdapterFactory.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package testGson;

import java.io.IOException;

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;


public enum FooAdapterFactory implements TypeAdapterFactory {
INSTANCE; // Josh Bloch's Enum singleton pattern

@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (!Foo.class.isAssignableFrom(type.getRawType())) return null;
// Note: You have access to the `gson` object here; you can access other deserializers using gson.getAdapter and pass them into your constructor
return (TypeAdapter<T>) new FooAdapter();
}

private static class FooAdapter extends TypeAdapter<Foo> {
@Override
public void write(JsonWriter out, Foo obj) {
try {
out.beginObject();
out.name("IdInteger").value(obj.getIdInteger());
out.name("NameString").value(obj.getNameString());
out.name("ValueString").value(obj.getValueString());
out.name("DataString").value(obj.getDataString());
out.endObject();
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public Foo read(JsonReader in) throws IOException {
final Foo obj = new Foo();
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "IdInteger":
obj.setIdInteger(in.nextInt());
break;
case "NameString":
obj.setNameString(in.nextString());
break;
case "ValueString":
obj.setValueString(in.nextString());
break;
case "DataString":
obj.setDataString(in.nextString());
break;
default:
break;

}
}
in.endObject();
return obj;
}
}
}

FactoryTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package testGson;

import java.io.File;
import java.io.IOException;

import static java.lang.System.out;

import org.apache.commons.io.FileUtils;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class FactoryTest {

public static void main(String[] args) {
Foo foo = new Foo();
/**
* 1. simple gson:
* */
/* Gson gson = new Gson();*/
/**
* 2. gson with TypeAdapter:
* */
Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(Foo.class, new
FooAdapter()) .create();
/**
* 3. gson with TypeAdapterFactory:
* */
/* Gson gson =new GsonBuilder().registerTypeAdapterFactory(FooAdapterFactory.INSTANCE).create();
*/
String jsonString = gson.toJson(foo);
File tempStore = new File("input/tempStore.json");
try {
FileUtils.writeStringToFile(tempStore, jsonString);
} catch (IOException e) {
e.printStackTrace();
}
Foo foo2=gson.fromJson(jsonString, Foo.class);
out.println(foo2.dataString);
out.println(foo2.idInteger);
out.println(foo2.nameString);
out.println(foo2.valueString);
}

}

maven pom.xml

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
<!--FileUtil和IOUtil-->
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3</version>
</dependency>

ParseTokenExample.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package testGson;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;



import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;



import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;

public class ParseTokenExample{
public static void main(String[] args) throws MalformedURLException, IOException {
String url = "http://freemusicarchive.org/api/get/albums.json?api_key=60BLHNQCAOUFPIBZ&limit=5";
String json = IOUtils.toString(new URL(url));
//使用reader去读取json
JsonReader reader = new JsonReader(new StringReader(json));
File file = new File("input/jsondata.json");
FileUtils.writeStringToFile(file, json);
// handleObject(reader);
}
private static void handleObject(JsonReader reader) throws IOException {
reader.beginObject();
while (reader.hasNext()) {
JsonToken token = reader.peek();
if (token.equals(JsonToken.BEGIN_ARRAY))
handleArray(reader);
else if (token.equals(JsonToken.END_ARRAY)) {
reader.endObject();
return;
} else
handleNonArrayToken(reader, token);
}

}
/**
   *处理json数组,第一个标记是 JsonToken_BEGIN_ARRAY,数组可能包含对象或者基本原始类型
*
* @param reader
* @throws IOException
*/
public static void handleArray(JsonReader reader) throws IOException {
reader.beginArray();
while (true) {
JsonToken token = reader.peek();
if (token.equals(JsonToken.END_ARRAY)) {
reader.endArray();
break;
} else if (token.equals(JsonToken.BEGIN_OBJECT)) {
handleObject(reader);
} else
handleNonArrayToken(reader, token);
}
}

/**
* 处理不是数组的符号标记
*
* @param reader
* @param token
* @throws IOException
*/
public static void handleNonArrayToken(JsonReader reader, JsonToken token) throws IOException {
if (token.equals(JsonToken.NAME))
System.out.println(reader.nextName());
else if (token.equals(JsonToken.STRING))
System.out.println(reader.nextString());
else if (token.equals(JsonToken.NUMBER))
System.out.println(reader.nextDouble());
else
reader.skipValue();
}
}
  1. 学习gson的adapter(看懂了上面代码就不用看这个了):
    http://www.javacreed.com/gson-typeadapter-example/

3.进阶学习(嵌套内部类,复杂类):
http://www.cnblogs.com/zhangminghui/p/4109539.html

个人想法:
最根本的办法是减少耦合,减少依赖关系,尽量存储id,而不是对象引用,这样能最大程度地减少复杂代码。

mybatis处理mysql自增主键的三种写法

  1. 数据库的自增主键id(int(11)),查看auto_increment,记录的是下一次插入时的主键值。(=最大的id+1)

    1
    2
    3
    SHOW TABLE STATUS;
    #或:
    SELECT AUTO_INCREMENT FROM information_schema.tables WHERE table_name='t_account_0'
  2. 测试select @@identity命令:

    1
    2
    INSERT into user_info  (uname, unumber) VALUES ('test','test');
    select @@identity as 'curMaxId';

    注意到第一条命令并没有插入id值,以便让数据库插入自增id值。
    两条命令同一线程中顺序执行后,第二条命令获取到第一条命令插入的id值。
    例如,若select @@identity结果为106,则auto_increment此时为107,而INSERT的记录id值为106.

  3. 第一种mybatis支持自增主键的写法,使用useGeneratedKeyskeyProperty属性设置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <sql id="UserInfoSet">
    <set>
    <if test="id != null">id=#{id},</if>
    <if test="uname!= null">uname=#{uname},</if>
    <if test="unumber != null">unumber=#{unumber},</if>
    </set>
    </sql>
    <insert id="insert" parameterType="UserInfo"
    useGeneratedKeys="true" keyProperty="id">
    insert user_info
    <include refid="UserInfoSet" />
    </insert>

    如果表中主键已经设定为auto_increment,则插入后会自动把生成的id值返回给插入的对象。

    1
    2
    3
    4
    5
    6
    UserInfo userInfo = new UserInfo();
    userInfo.setUname("xiaoming");
    userInfo.setUnumber("x07");
    //a.插入前userinfo的id为null
    result = userService.insert(userInfo);
    //b.插入后userinfo的id为数据库生成的id值(例如123).
  4. 第二种使用selectKey,限定于mysql数据库的写法,其他数据库如oraclebefore,各有不同:

    1
    2
    3
    4
    5
    6
    <insert id="insert" parameterType="UserInfo">
    <selectKey resultType="java.lang.Integer" keyProperty="id" order="AFTER">
    SELECT @@IDENTITY
    </selectKey>
    insert user_info <include refid="UserInfoSet" />
    </insert>

    此时表现和上一种一样;
    若把SELECT @@IDENTITY改为 select 20 from dual , 则数据库中虽依然插入了正常递增的id值(如123),
    b处插入后的对象中id值为20. 可见selectKey标签实际执行是在insert执行后,返回obj对象前,将其中的id值改为20.并不影响insert执行过程。
    综上所示,这两种方法其实都不影响insert,只是执行后获取一次id值。insert过程中生成id值是由数据库支持的。
    所以如果数据库中没有设定主键的自增属性,这两种都是无能为力的,此时就要更改SelectKey的用法:

  5. 第三种,使用select max(id)+1

    1
    2
    3
    4
    5
    6
    <insert id="insert" parameterType="test.model.UserInfo" >
    <selectKey resultType="java.lang.Integer" keyProperty="id" order="BEFORE">
    select max(id)+1 from user_info
    </selectKey>
    insert user_info <include refid="UserInfoSet" />
    </insert>

    其中select max(id)+1 from user_info语句是选出表中最大id值+1,并赋予传入的参数userInfo对象,然后再执行insert.
    注意到order此时等于before. 应当警惕的是多用户交替插入时,这种方法的安全性,涉及到事务比较麻烦,所以最好还是直接设计数据库时直接设定主键的递增属性。

其他感想:
mybatischoose when好比switch case;
otherwise就是defaultforeach就是传入一个listarraymybatis,然后生成很长的sql
学到这里大概觉得mybatis生成sql,就跟jsp生成html一样,就是输出,就是翻译,编译原理无处不在,重复造轮子无处不在。
暂时不适用trim标签了,太geek了,而且居然只有replace功能,可读性差。

ubuntu松耦合配置jdk和终端快捷键

  • 终端快捷键:
    新做系统后,为了提高工作效率,安装右键打开终端快捷键:

    1
    2
    sudo apt-get install nautilus-open-terminal
    nautilus -q

    注销后登录,可以右键+e,在当前路径打开终端,比ctrl+alt+T顺手一点。而且ctrl+alt+T只能在~路径下打开终端,不够灵活。

  • jdk:
    首先搜索jdk后进入官网下载最新版64位jdk。(多用tab快捷键补全。)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    tar zxvf jdk(alt+/)
    sudo mkdir /usr/lib/jdk/
    sudo mv jdk1.8.0_45/ /usr/lib/jdk/jdk1.8.0_45
    cd /usr/lib/jdk/
    sudo ln -s jdk(tab) jdk_current
    cd /etc/profile.d
    sudo vi java.sh
    export JAVA_HOME=/usr/lib/jdk/jdk_current
    export CLASSPATH=.:%JAVA_HOME%/lib/dt.jar:%JAVA_HOME%/lib/tools.jar
    export PATH=$PATH:$JAVA_HOME/bin
    source java.sh

    注销后登录, javajavac命令可以使用。这样做有几个好处:

  • 更换jdk时,只需将jdk_current软链接指向新的jdk路径即可。

  • 与别的环境变量耦合度较低,以此类推每个新的环境变量可以下/etc/profile.d路径下建立新的hadoop.sh,linux系统启动时会自动加载此目录下的*.sh文件。

  • 可以把java.sh文件和jdk文件夹拷贝到别的电脑的/etc/profile.d路径下以添加jdk的环境变量。

  • 配置root用户图形界面登录

    1
    2
    3
    4
    vi /etc/lightdm/lightdm.conf
    # 添加:
    greeter-show-manual-login=true
    allow-guest=false
  • 自动挂载新增的磁盘

    1
    2
    3
    4
    5
    6
    7
    8
    # 安装gparted
    sudo apt-get install gparted
    # 以root用户进入图形界面
    # gparted配置分区, 查看ssd磁盘的uuid,然后修改文件以自动挂载新增的盘:
    vi /etc/fstab
    # 末尾添加一行:
    UUID=8436117e-d922-452d-8d86-56edb76b46f6 /home/xiaoyue26/ssd ext4 defaults 0 2
    # 上述格式为: UUID 挂载点 文件系统 策略 <dump> <pass>

default参数说明按照默认格式挂载.
其他参数:

1
2
3
4
5
6
7
auto: 开机自动挂载 
noauto: 开机不自动挂载
defaults: 按照大多数永久文件系统的缺省值设置挂载定义
ro: 按只读权限挂载
rw: 按可读可写权限挂载
user: 任何用户都可以挂载
user: 同步磁盘与内存中的数据,async 则是异步

dump处为1的话,表示要将整个文件系统里的内容备份;
现在很少用到dump这个工具,在这里一般选0。0表示不做dump备份,1表示要进行dump备份,2也表示要做dump备份,不过,该分区的重要行比1小。

pass用来指定如何使用fsck来检查硬盘。如果这里填 0,则不检查;挂载点为 / 的(即根分区),必须在这里填写 1,其它的都不能填写 1。
如果有分区填写大于 1的话,则在检查完根分区后,接着按填写的数字从小到大依次检查下去。同数字的同时检查。

  • 退出保存,检查语法是否正确并且自动挂载盘:
    1
    2
    3
    4
    5
    mount -a 
    # 挂好以后, 递归更改一下权限:
    chmod -R 775 /home/xiaoyue26/ssd #更改读写权限
    chown -R xiaoyue26 /home/xiaoyue26/ssd #更改拥有者
    chgrp -R xiaoyue26 /home/xiaoyue26/ssd #更高所属组

springmvc笔记

  1. 调用顺序:
    (1)以web.xml为入口,(context-param里各种环境的配置)编码由filter-mapping拦截,servlet-mapping匹配servlet所服务的url模式,例如当此servlet加载配置classpath:spring-mvc.xml之后:
    (2)扫描base-package下的controllercontroller@RequestMapping匹配url模式,调用service,调用service.impl,调用Mapper(DAO),调用mapping包中mapper.xml中的sql语句。
    所以编写顺序:
    1
    2
    3
    4
    5
    mapper.xml
    Mapper(DAO)
    service
    service.impl
    controller
    调用顺序:
    1
    2
    3
    4
    controller
    service.impl
    Mapper(DAO)
    mapper.xml
  2. 需要检查注解的地方:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Controller:
    @Controller

    @RequestMapping("/user") 类

    @Autowired (service)

    @RequestMapping("/showInfo/{userId}") 方法
    #两层递进的request mapping,url可以不止两层,因为方法前的url可以很多斜杠,类前的不知道。

    service.impl:

    @Service("userService")

    @Autowired mapper
    方法前注解记录:
    1
    2
    3
    4
    @ResponseBody
    标注后,返回String对象的结果为response内容体,不标注的话,作为dispatcher url使用。
    @PathVariable
    允许将请求路径的一部分当做方法的传入形参使用
    方法的返回值:
    1
    2
    3
    4
    5
    6
    void: 
    此时逻辑视图名由请求处理方法对应的URL确定,如请求welcome,则返回welcome.jsp
    String:
    此时逻辑视图名为返回的字符,如return "/user/showInfo";则返回/user/showInfo.jsp。
    ModelMap:
    跟void一样。
    1
    2
    3
    4
    5
    spring-context:
    1.将Cinema.java的头部标注为@Component说明该类交由Spring托管
    2.Cinema.java中的属性MoviceService标注为@Autowired,则Spring在初始化Cinema类时会从Application Context中找到类型为MovieService的Bean,并赋值给Cinema
    3.在Application.java中我们声明了一个类型为MovieService的Bean。并且标注Application.java为@Configuration,这是告诉Spring在Application.java中定义了一个或多个@Bean方法,让Spring容器可以在运行时生成这些Bean。
    4.@ComponentScan则会让Spring容器自动扫描当前package下的标有@Component的class,这些class都将由Spring托管。
    此外还有未开启的注解,测试项目中未能涉及。
    运行步骤:
    1
    2
    3
    4
    5
    1. maven build, goal: clean compile package
    2. run on server
    3. http://localhost:8080/sunday/user/showInfos.htmls
    4. http://localhost:8080/sunday/user/showInfo/1.htmls 显示id为1的数据
    http://localhost:8080/sunday/user/showInfo/4.htmls 显示id为4的数据
  3. Sql_map中带有的两个函数是:
    1
    2
    updateByPrimaryKeySelective
    updateByPrimaryKey
    前者只是更新新的model中不为空的字段。
    后者则会将为空的字段在数据库中置为NULL。

http://blog.csdn.net/hao947_hao947/article/details/34594695?utm_source=tuicool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
resultMap :  
id:resultMap的唯一标识
type:映射实体类的数据类型
property:实体类里的属性名
column:库表的字段名
<!--id:当前sql的唯一标识
parameterType:输入参数的数据类型
resultType:返回值的数据类型
#{}:用来接受参数的,如果是传递一个参数#{id}内容任意,如果是多个参数就有一定的规则,采用的是预编译的形式
select * from person p where p.id = ? ,安全性很高 -->

<!-- sql语句返回值类型使用resultMap -->
<select id="selectPersonById" parameterType="java.lang.Integer"
resultMap="BaseResultMap">
select * from person p where p.person_id = #{id}
</select>
<!-- resultMap:适合使用返回值是自定义实体类的情况
resultType:适合使用返回值的数据类型是非自定义的,即jdk的提供的类型 -->
<select id="selectPersonCount" resultType="java.lang.Integer">
select count(*) from
person
</select>

<select id="selectPersonByIdWithMap" parameterType="java.lang.Integer"
resultType="java.util.Map">
select * from person p where p.person_id= #{id}
</select>

http://www.open-open.com/lib/view/open1325862592062.html
ofType也是表示返回类型,这里的返回类型是集合内部的类型,之所以用ofType而不是用type是MyBatis内部为了和关联association进行区别。

1
2
3
4
5
6
7
8
9
10
<resultMap type="Blog" id="BlogResult">

<id column="id" property="id"/>
<collection property="comments" select="selectCommentsByBlog" column="id" ofType="Comment"/>
</resultMap>

<resultMap type="Comment" id="CommentResult">
<association property="blog" javaType="Blog" column="blog" select="selectBlog"/>

</resultMap>
注意collection和association,ofType和javaType的不同。此处column="blog"中的blog为外键名。

第一次猜测#{title}中引用的应该是传入参数对象的实体类中的属性名。(应该不是数据库中的列名。)
比如传入参数类型是Blog,#{blogid}就应该是Blog类的blogid属性。 

在看http://zhuyuehua.iteye.com/blog/1721715之后:
1
2
3
4
5
6
7
8
<select id="selectRole" parameterType="java.lang.Long" resultType="Role" >     
select * from Role where id =#{id}
</select>
selectRole的SQL输入参数可以随便给名称,只要是输入参数与压入进去的值类型相同就行了,可以写成:
select * from Role where id = #{sfffs}
mybatis最终会执行:select * from role where id =resultSet.getLong("role_id");
其中传入参数的语句可能是:
<association property="role" column="role_id" javaType="Role" select="selectRole"/>

存疑。
继续看http://zhuyuehua.iteye.com/blog/1721715。

maven3笔记

  1. 从maven官网下载maven压缩包,解压缩,配置MAVEN_HOME
  2. 最新版的eclipse(mars) j2ee版本直接集成了maven插件m2e;
  3. [ERROR] No plugin found for prefix 'archetype' in the current project and ..
    conf/setting中加入镜像地址:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <mirror> 
    <id>ibiblio.org</id>
    <name>ibiblio Mirror of http://repo1.maven.org/maven2/</name>
    <url>http://mirrors.ibiblio.org/pub/mirrors/maven2</url>
    <mirrorOf>central</mirrorOf>
    <!-- United States, North Carolina -->
    </mirror>
    <mirror>
    <id>jboss-public-repository-group</id>
    <mirrorOf>central</mirrorOf>
    <name>JBoss Public Repository Group</name>
    <url>http://repository.jboss.org/nexus/content/groups/public</url>
    </mirror>
  4. 若maven的conf下setting.xml文件中修改了本地仓库reposity的路径,则在eclipse中也要在windows->prefrence->maven->user settings中相应修改路径。

参考链接:
http://www.cnblogs.com/leiOOlei/p/3376155.html
http://blog.csdn.net/hay24/article/details/8246203
http://mvnrepository.com/artifact/org.springframework/spring-context

tesseract_ocr_centos6.5

1.安装依赖:

1
2
3
4
5
6
sudo yum -y groupinstall "Development Tools"
sudo yum -y gcc gcc-c++ kernel-devel make
sudo yum -y install libpng-devel.x86_64
sudo yum -y install libjpeg-devel.x86_64
sudo yum -y install libtiff-devel.x86_64
sudo yum -y install zlib-devel.x86_64

2.下载leptonica和tesseract:

https://code.google.com/p/leptonica/downloads/list
https://code.google.com/p/tesseract-ocr/downloads/list
http://www.leptonica.org/download.html
https://code.google.com/p/tesseract-ocr/


3.解压下载好的压缩包:
tesseract-ocr-3.02.02.tar.gz
leptonica-1.72.tar.gz
tesseract-ocr-3.02.eng.tar.gz
4.进入leptonica目录编译安装:

1
2
3
./configure
make
sudo make install

5.进入tesseract-ocr目录编译安装:

1
2
3
4
5
./autogen.sh
./configure
make
sudo make install
sudo ldconfig

6.解压语言包tesseract-ocr-3.02.eng.tar.gz
解压后将tesseract-ocr/tessdata 下的所有文件全部拷贝到 /usr/local/share/tessdata 下。

7.测试安装成功:
进入tesseract-ocr目录:

1
tesseract phototest.tif phototest -l eng

若生成phototest.txt,且其中内容与phototest.tif相同则成功。

vagrant笔记

##操作环境为win7物理机vmware中的Ubuntu12.04.5 amd64,客户机自带virtural box.
1.直接安装:

1
2
sudo apt-get install ruby rubygems
sudo apt-get install vagrant#1.x版本

或者官网下载最新版然后安装:

1
sudo dpkg -i vagrant_1.7.4_x86_64.deb

从vagrant的[box列表][1]下载一个base box,例如presice32.box。
(由于笔者的虚拟机没法再次虚拟化64位的系统,所以选用了32位box。猜测应该是没有打开cpu或者bios的虚拟化设置。)
首先移动到工作目录,如~/vagrant_workplace/boxes/ubuntu12.04
##从本地添加一个box,并命名为ubuntu12.04:
vagrant box add ubuntu12.04 ./precise32.box
##初始化box:
vagrant init ubuntu12.04
##启动虚拟机box:
vagrant up
##ssh登录到box:
vagrant ssh
##重启:
vagrant reload
##打包分发 以虚拟机ubuntu12.04为Base创建myubuntu32.box:
vagrant package –output myubuntu32.box –base ubuntu12.04
##关闭虚拟机
vagrant halt
##销毁虚拟机
vagrant destroy
##移除添加的box:
vagrant box remove ubuntu12.04

box下载列表:
http://www.vagrantbox.es/
https://atlas.hashicorp.com/boxes/search?utm_source=vagrantcloud.com&vagrantcloud=1

下一步学stash
[1]: http://www.vagrantbox.es/