15My3.0版本聊天系统(mybatis集成)
来源:互联网 发布:移动光纤网络机房在哪 编辑:程序博客网 时间:2024/06/07 07:09
/** * * @author Sillent_Hill * * MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。 * MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。 * MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 * POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。 * * 利用mybatis以及代码自动生成工具产生有: * com.bean.ChatUser (javabean对象) * com.bean.OffMsg (javabean对象) * com.dao.ChatUserMapper (对应接口) * com.dao.OffMsgMapper (对应接口) * ChatUserMapper.xml (对应接口的mysql语句配置) * OffMsgMapper.xml (对应接口的mysql语句配置) * */public class ChatUser { private String username; private String password; private String phonenumber; private String address; public ChatUser() {super();// TODO Auto-generated constructor stub}public ChatUser(String username, String password, String phonenumber, String address) {super();this.username = username;this.password = password;this.phonenumber = phonenumber;this.address = address;}public String getUsername() { return username; } public void setUsername(String username) { this.username = username == null ? null : username.trim(); } public String getPassword() { return password; } public void setPassword(String password) { this.password = password == null ? null : password.trim(); } public String getPhonenumber() { return phonenumber; } public void setPhonenumber(String phonenumber) { this.phonenumber = phonenumber == null ? null : phonenumber.trim(); } public String getAddress() { return address; } public void setAddress(String address) { this.address = address == null ? null : address.trim(); }}public class OffMsg { private String username; private String userfrom; private String sendtime; private String message; public OffMsg() {super();// TODO Auto-generated constructor stub}public OffMsg(String username, String userfrom, String sendtime, String message) {super();this.username = username;this.userfrom = userfrom;this.sendtime = sendtime;this.message = message;}public String getUsername() { return username; } public void setUsername(String username) { this.username = username == null ? null : username.trim(); } public String getUserfrom() { return userfrom; } public void setUserfrom(String userfrom) { this.userfrom = userfrom == null ? null : userfrom.trim(); } public String getSendtime() { return sendtime; } public void setSendtime(String sendtime) { this.sendtime = sendtime == null ? null : sendtime.trim(); } public String getMessage() { return message; } public void setMessage(String message) { this.message = message == null ? null : message.trim(); }}public interface ChatUserMapper { int deleteByPrimaryKey(String username); int insert(ChatUser record); int insertSelective(ChatUser record); ChatUser selectByPrimaryKey(String username); int updateByPrimaryKeySelective(ChatUser record); int updateByPrimaryKey(ChatUser record); List<ChatUser> getAllChatUsers();}public interface OffMsgMapper { int insert(OffMsg record); int insertSelective(OffMsg record); List<OffMsg> checkOffMsg(String username); void delOffMsg(String username);}<!-- com.mapper.ChatUserMapper.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.dao.ChatUserMapper" > <resultMap id="BaseResultMap" type="com.bean.ChatUser" > <id column="username" property="username" jdbcType="VARCHAR" /> <result column="password" property="password" jdbcType="VARCHAR" /> <result column="phonenumber" property="phonenumber" jdbcType="VARCHAR" /> <result column="address" property="address" jdbcType="VARCHAR" /> </resultMap> <sql id="Base_Column_List" > username, password, phonenumber, address </sql> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" > select <include refid="Base_Column_List" /> from chatuser where username = #{username,jdbcType=VARCHAR} </select> <select id="getAllChatUsers" resultMap="BaseResultMap" > select * from chatuser </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.String" > delete from chatuser where username = #{username,jdbcType=VARCHAR} </delete> <insert id="insert" parameterType="com.bean.ChatUser" > insert into chatuser (username, password, phonenumber, address) values (#{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{phonenumber,jdbcType=VARCHAR}, #{address,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" parameterType="com.bean.ChatUser" > insert into chatuser <trim prefix="(" suffix=")" suffixOverrides="," > <if test="username != null" > username, </if> <if test="password != null" > password, </if> <if test="phonenumber != null" > phonenumber, </if> <if test="address != null" > address, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides="," > <if test="username != null" > #{username,jdbcType=VARCHAR}, </if> <if test="password != null" > #{password,jdbcType=VARCHAR}, </if> <if test="phonenumber != null" > #{phonenumber,jdbcType=VARCHAR}, </if> <if test="address != null" > #{address,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="com.bean.ChatUser" > update chatuser <set > <if test="password != null" > password = #{password,jdbcType=VARCHAR}, </if> <if test="phonenumber != null" > phonenumber = #{phonenumber,jdbcType=VARCHAR}, </if> <if test="address != null" > address = #{address,jdbcType=VARCHAR}, </if> </set> where username = #{username,jdbcType=VARCHAR} </update> <update id="updateByPrimaryKey" parameterType="com.bean.ChatUser" > update chatuser set password = #{password,jdbcType=VARCHAR}, phonenumber = #{phonenumber,jdbcType=VARCHAR}, address = #{address,jdbcType=VARCHAR} where username = #{username,jdbcType=VARCHAR} </update></mapper><!-- com.mapper.OffMsgMapper.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.dao.OffMsgMapper" > <resultMap id="BaseResultMap" type="com.bean.OffMsg" > <result column="username" property="username" jdbcType="VARCHAR" /> <result column="userfrom" property="userfrom" jdbcType="VARCHAR" /> <result column="sendtime" property="sendtime" jdbcType="VARCHAR" /> <result column="message" property="message" jdbcType="VARCHAR" /> </resultMap> <insert id="insert" parameterType="com.bean.OffMsg" > insert into offmsg (username, userfrom, sendtime, message) values (#{username,jdbcType=VARCHAR}, #{userfrom,jdbcType=VARCHAR}, #{sendtime,jdbcType=VARCHAR}, #{message,jdbcType=VARCHAR}) </insert> <!-- 检查该用户是否存在离线消息 --> <select id="checkOffMsg" parameterType="java.lang.String" resultType="com.bean.OffMsg" > select * from offmsg where username = #{username} </select> <!-- 删除该用户的离线消息 --> <delete id="delOffMsg" parameterType="java.lang.String"> delete from offmsg where username=#{username} </delete> <insert id="insertSelective" parameterType="com.bean.OffMsg" > insert into offmsg <trim prefix="(" suffix=")" suffixOverrides="," > <if test="username != null" > username, </if> <if test="userfrom != null" > userfrom, </if> <if test="sendtime != null" > sendtime, </if> <if test="message != null" > message, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides="," > <if test="username != null" > #{username,jdbcType=VARCHAR}, </if> <if test="userfrom != null" > #{userfrom,jdbcType=VARCHAR}, </if> <if test="sendtime != null" > #{sendtime,jdbcType=VARCHAR}, </if> <if test="message != null" > #{message,jdbcType=VARCHAR}, </if> </trim> </insert></mapper>
mybatis主要配置文件
<?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> <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/school" /> <property name="username" value="root" /> <property name="password" value="111111" /> </dataSource> </environment> </environments> <mappers> <!-- mybatis以xml配置文件方式实现db操作 --> <!-- mapper映射器:包含了sql代码和映射定义信息 --> <mapper resource="com/mapper/ChatUserMapper.xml"/> <mapper resource="com/mapper/OffMsgMapper.xml"/> </mappers></configuration>
产生SqlSessioniFactory对象的线程安全的懒汉式单例类
public class MySqlSessionFactory {private static SqlSessionFactory sqlSessionFactory;private MySqlSessionFactory(){}public static synchronized SqlSessionFactory getInstance(){if (sqlSessionFactory == null){String resource = "com/config/batisconfig.xml";try {InputStream is = Resources.getResourceAsStream(resource);sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);} catch (IOException e){e.printStackTrace();}}return sqlSessionFactory;}}
服务器端
package nioserver;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Collections;import java.util.Date;import java.util.Iterator;import java.util.List;import java.util.Scanner;import java.util.Set;import java.util.Map.Entry;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import org.apache.ibatis.session.SqlSession;import com.bean.ChatUser;import com.bean.OffMsg;import com.dao.ChatUserMapper;import com.dao.OffMsgMapper;import com.google.gson.JsonElement;import com.google.gson.JsonObject;import com.google.gson.JsonParser;import com.sql.MySqlSessionFactory;/** * * @author Sillent_Hill * * 问题总结: * * 1.PrintWriter out = new PrintWriter(client.getOutputStream(), true); * 当out.close(),会是的client不能正常工作,第二个参数true使得每次发送的消息 * 能够及时从缓存中刷新出去. * * 2.判断多个程序登陆同一账号时,NioServer.map.get()需要在NioServer.map.put() * 方法之前,判断用户是否已经登陆,并且在登陆的情况下可以方便得到登陆信息 * * 3.使用mybatis创建的线程安全的懒汉式单例类中 * sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); * 要确保is读取的.xml文件及其相关联的其他.xml文件的正确性,才能正常工作 * * 4.为了将应用层和持久层分隔开,应用程序在对数据库进行操作之前必须 * 确保参数的正确性 * */class WorkTask implements Runnable{private Selector selector;private List<SocketChannel> list;private ByteBuffer buffer;public WorkTask() throws IOException{selector = Selector.open();list = Collections.synchronizedList(new ArrayList<SocketChannel>());buffer = ByteBuffer.allocate(1024);}public List<SocketChannel> getList(){return list;}public Selector getSelector(){return selector;}public String getCurTime(){Date date = new Date(System.currentTimeMillis());SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return sdf.format(date);}//访问数据库检查username,password是否正确public void login(SocketChannel cChannel, JsonObject jobject){SqlSession session = null;try {String username = jobject.get("name").getAsString();String password = jobject.get("pwd").getAsString();boolean bloginstate = false;session = MySqlSessionFactory.getInstance().openSession(true);ChatUserMapper usermapper = session.getMapper(ChatUserMapper.class);ChatUser user = usermapper.selectByPrimaryKey(username);//给客户端回复json字符串,20表示服务器的响应登陆消息JsonObject json = new JsonObject(); json.addProperty("msgtype", 20); //验证账号和密码并判断是否存在多个客户端同时登陆一个账号if (user != null && user.getPassword().equals(password)){ SocketChannel socketchannel = NioServer.map.get(username);if (socketchannel == null){ NioServer.map.put(username, cChannel);bloginstate = true;json.addProperty("ack", "loginok");}else{json.addProperty("ack", "loginfail");json.addProperty("reason", "your acount has been logged at " + socketchannel.getRemoteAddress());}}else{json.addProperty("ack", "loginfail");json.addProperty("reason", "username or password is wrong!!!");}cChannel.write(ByteBuffer.wrap((json.toString()+"\n").getBytes()));//登陆失败,直接返回if (!bloginstate){ return;}//在线登陆提醒服务userLoginCall(username);//检查是否存在离线消息,16表示消息类型为离线消息OffMsgMapper offmsgmapper = session.getMapper(OffMsgMapper.class);List<OffMsg> list = offmsgmapper.checkOffMsg(username);if (list.size() == 0){return;}for (OffMsg offmsg : list){JsonObject offjson = new JsonObject();offjson.addProperty("msgtype", 16);String userfrom = offmsg.getUserfrom();if (userfrom.equals("SuperUser")){offjson.addProperty("ack", "servermsg"); //系统管理员发送的离线消息}else{offjson.addProperty("ack", "usermsg");offjson.addProperty("userfrom", userfrom); //普通用户发送的离线消息}offjson.addProperty("sendtime", offmsg.getSendtime());offjson.addProperty("message", offmsg.getMessage());cChannel.write(ByteBuffer.wrap((offjson.toString()+"\n").getBytes()));}offmsgmapper.delOffMsg(username);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally{if (session != null){session.close();}}}//检验注册信息是否合法,访问数据库,完成用户注册public void register(SocketChannel cChannel, JsonObject jobject){SqlSession session = null;try {String name = jobject.get("name").getAsString();String pwd = jobject.get("pwd").getAsString();String pnumber = jobject.get("pnumber").getAsString();String addr = jobject.get("addr").getAsString();//19表示服务器响应客户端注册是否成功JsonObject json = new JsonObject();json.addProperty("msgtype", 19);if (name.length() > 20 || pwd.length() > 20 ||pnumber.length() > 20 || addr.length() > 20){json.addProperty("ack", "data_long_error");json.addProperty("reason", "input data too long!!!");cChannel.write(ByteBuffer.wrap((json.toString()+"\n").getBytes()));return;}session = MySqlSessionFactory.getInstance().openSession(true);ChatUserMapper usermapper = session.getMapper(ChatUserMapper.class);ChatUser retuser = usermapper.selectByPrimaryKey(name);if (retuser != null){json.addProperty("ack", "pri_error");json.addProperty("reason", "username has been userd!!!");cChannel.write(ByteBuffer.wrap((json.toString()+"\n").getBytes()));return;}ChatUser user = new ChatUser(name, pwd, pnumber, addr);int result = usermapper.insert(user);if (result == 1){json.addProperty("ack", "reg_ok");}else{json.addProperty("ack", "reg_error");}cChannel.write(ByteBuffer.wrap((json.toString()+"\n").getBytes()));} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally{if (session != null){session.close();}}}//完成向目标用户聊天消息的转发public void chat(SocketChannel cChannel, JsonObject jobject){try{String userfrom = jobject.get("userfrom").getAsString();String sendto = jobject.get("sendto").getAsString();String msg = jobject.get("msg").getAsString();String time = getCurTime();//18表示服务器响应发送方消息是否发送成功JsonObject json = new JsonObject();json.addProperty("msgtype", 18);if (msg.length() > 30){json.addProperty("ack", "msg_long_error");cChannel.write(ByteBuffer.wrap((json.toString()+"\n").getBytes()));return;}//访问接收消息的用户是否存在boolean userexist = checkExist(sendto); if (!userexist){json.addProperty("ack", "exist_error");cChannel.write(ByteBuffer.wrap((json.toString()+"\n").getBytes()));return;}//检查接收消息的用户是否在线SocketChannel sendToClient = NioServer.map.get(sendto);if (sendToClient == null){json.addProperty("ack", "online_error");json.addProperty("sendto", sendto);cChannel.write(ByteBuffer.wrap((json.toString()+"\n").getBytes()));//将离线消息存储进数据库saveMessage(userfrom,sendto, time, msg);return;}//转发消息,17表示客户端接收来自其他用户的发送的消息JsonObject sendjson = new JsonObject(); sendjson.addProperty("msgtype", 17);sendjson.addProperty("ack", "usermsg");sendjson.addProperty("userfrom", userfrom);sendjson.addProperty("sendtime", time);sendjson.addProperty("msg", msg);sendToClient.write(ByteBuffer.wrap((sendjson.toString()+"\n").getBytes()));//回复发送方,表示信息发送成功json.addProperty("ack", "send_ok");cChannel.write(ByteBuffer.wrap((json.toString()+"\n").getBytes()));}catch (IOException e){e.printStackTrace();}}private void saveMessage(String userfrom, String sendto, String time, String msg){SqlSession session = MySqlSessionFactory.getInstance().openSession(true);OffMsgMapper offmsgmapper = session.getMapper(OffMsgMapper.class);OffMsg offmsg = new OffMsg(sendto, userfrom, time, msg);offmsgmapper.insert(offmsg);session.close();}//用户上线提醒private void userLoginCall(String name) throws IOException{String time = getCurTime();Set<Entry<String, SocketChannel>> users = NioServer.map.entrySet();for (Entry<String, SocketChannel> user : users){JsonObject onlineCall = new JsonObject();onlineCall.addProperty("msgtype", 17);onlineCall.addProperty("ack", "servermsg");onlineCall.addProperty("sendtime", time);onlineCall.addProperty("message", "user " + name + " has logged in!");String recvUserName = user.getKey();if (recvUserName.compareTo(name) != 0){user.getValue().write(ByteBuffer.wrap((onlineCall.toString()+"\n").getBytes()));}}}//用户下线提醒private void userExitCall(String name){String time = getCurTime();try{Set<Entry<String, SocketChannel>> users = NioServer.map.entrySet();for (Entry<String, SocketChannel> user : users){JsonObject offlineCall = new JsonObject();offlineCall.addProperty("msgtype", 17);offlineCall.addProperty("ack", "servermsg");offlineCall.addProperty("sendtime", time);offlineCall.addProperty("message", "user " + name + " has quit out!"); user.getValue().write(ByteBuffer.wrap((offlineCall.toString()+"\n").getBytes()));}}catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}//检查该用户是否存在private boolean checkExist(String sendto){SqlSession session = MySqlSessionFactory.getInstance().openSession(true);ChatUserMapper chatuser = session.getMapper(ChatUserMapper.class);ChatUser user = chatuser.selectByPrimaryKey(sendto);session.close();if (user == null)return false;return true;}//删除map中指定SocketChannel并返回usernameprivate String removeUser(SocketChannel cChannel){if (cChannel != null){Iterator<Entry<String, SocketChannel>> it = NioServer.map.entrySet().iterator();while (it.hasNext()){Entry<String, SocketChannel> entry = it.next();if (entry.getValue().equals(cChannel)){String username = entry.getKey();NioServer.map.remove(username);return username;}}}return null;}private void userExit(SocketChannel cChannel, SelectionKey key) throws IOException{//删除map表中用户String exitusername = removeUser(cChannel);//下线提醒if (exitusername != null){userExitCall(exitusername);}cChannel.close();key.cancel();}@Overridepublic void run() {// TODO Auto-generated method stubtry{while (!Thread.currentThread().isInterrupted()){int num = selector.select();if (num <= 0){Iterator<SocketChannel> it = list.iterator();while (it.hasNext()){SocketChannel cChannel = it.next();cChannel.register(selector, SelectionKey.OP_READ);it.remove();}continue;}Iterator<SelectionKey> it = selector.selectedKeys().iterator();while (it.hasNext()){SelectionKey key = it.next();it.remove();if (key.isValid() && key.isReadable()){SocketChannel cChannel = (SocketChannel) key.channel();try{int readcnt = cChannel.read(buffer);//客户端正常关闭if (readcnt <= 0){userExit(cChannel, key);continue;}byte[] recvbuf = new byte[readcnt];System.arraycopy(buffer.array(), 0, recvbuf, 0, readcnt);buffer.flip();buffer.clear();String recvMsg = new String(recvbuf).trim();System.out.println("recvMsg:" + recvMsg);JsonParser parser = new JsonParser();JsonElement element = parser.parse(recvMsg);JsonObject jobject = element.getAsJsonObject();int msgtype = jobject.get("msgtype").getAsInt();switch(msgtype){case 1:login(cChannel, jobject);break;case 2:register(cChannel, jobject);break;case 3:chat(cChannel, jobject);break;}}catch(IOException e){//客户端异常关闭userExit(cChannel, key);}}}}}catch (IOException e){e.printStackTrace();}}}class WorkMenu implements Runnable{private Scanner scan;public WorkMenu(){scan = new Scanner(System.in);}public void menu(){System.out.println("--------------");System.out.println("1.广播消息");System.out.println("2....待增加");System.out.println("--------------");}public String getCurTime(){Date date = new Date(System.currentTimeMillis());SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return sdf.format(date);}//给所有在线或者不在线用户发送广播消息public void broadcastMsg(String broadMsg){SqlSession session = MySqlSessionFactory.getInstance().openSession(true);ChatUserMapper chatuser = session.getMapper(ChatUserMapper.class);List<ChatUser> list = chatuser.getAllChatUsers();String time = getCurTime();try {for (ChatUser user : list){String sendto = user.getUsername();//判断接收消息的用户是否在线SocketChannel client = NioServer.map.get(sendto);if (client != null){JsonObject json = new JsonObject();json.addProperty("msgtype", 17); //17表示客户接收在线消息json.addProperty("ack", "servermsg");json.addProperty("sendtime", time);json.addProperty("message", broadMsg);client.write(ByteBuffer.wrap((json.toString()+"\n").getBytes()));}else{//存储离线消息OffMsgMapper offmsgmapper = session.getMapper(OffMsgMapper.class);OffMsg offmsg = new OffMsg(sendto, "SuperUser", time, broadMsg);offmsgmapper.insert(offmsg);}}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally{session.close();}}@Overridepublic void run() {// TODO Auto-generated method stubmenu();int select = 0;while ((select = Integer.parseInt(scan.nextLine())) != 0){switch(select){case 1: //发送广播消息System.out.print("input broadcast message: ");String broadMsg = scan.nextLine();if (broadMsg.length() > 30){System.out.println("input message too long!");}else{broadcastMsg(broadMsg);}break;}menu();}}}public class NioServer {private Selector selector;private ServerSocketChannel sschannel;private WorkTask worktask;private static ExecutorService threadpool;public static ConcurrentHashMap<String, SocketChannel> map; static{threadpool = Executors.newFixedThreadPool(2);map = new ConcurrentHashMap<String, SocketChannel>();}public NioServer() throws IOException{selector = Selector.open();sschannel = ServerSocketChannel.open();sschannel.bind(new InetSocketAddress("127.0.0.1", 6000));sschannel.configureBlocking(false);sschannel.register(selector, SelectionKey.OP_ACCEPT);worktask = new WorkTask();threadpool.submit(worktask);threadpool.submit(new WorkMenu());}public void startServer() throws IOException{System.out.println("server supply service on 6000...");while (!Thread.currentThread().isInterrupted()){int num = selector.select();if (num <= 0){continue;}Iterator<SelectionKey> it = selector.selectedKeys().iterator();while (it.hasNext()){SelectionKey key = it.next();it.remove();if (key.isValid() && key.isAcceptable()){SocketChannel cChannel = sschannel.accept();cChannel.configureBlocking(false);worktask.getList().add(cChannel);worktask.getSelector().wakeup();}}}}public static void main(String[] args) throws IOException {// TODO Auto-generated method stubNioServer server = new NioServer();server.startServer();}}
0 0
- 15My3.0版本聊天系统(mybatis集成)
- 12My1.0版本聊天系统
- SpringMvc+Mybatis企业级集成Websocket在线聊天
- 14My2.0版本聊天系统(服务器selector移植)
- Mybatis无缝集成Memcached分布式缓存系统
- Spring集成Mybatis(事务处理)较全版本
- 点对点聊天系统 v1.0
- Mybatis集成
- 集成mybatis
- 融云聊天集成
- 聊天系统
- 聊天系统
- 聊天系统
- 聊天机器人--基础版本
- 如何集成一套并发版本控制管理系统
- 简单的聊天系统Chat1.0
- spring boot+mvc+mybatis+netty-sokey.io+html+js实现简单即时通讯聊天系统
- 集成环信实现聊天
- WinterEx.监测点
- Java通信之客户端的创建以及客户端和服务器的简单交互
- Git和Github的基本使用
- sql server 2008 r2中用alter修改表结构---给属性加上unique约束
- 常用Linux操作
- 15My3.0版本聊天系统(mybatis集成)
- php的Generator生成器及yield
- Leetcode 166. Fraction to Recurring Decimal
- CSS基础入门3
- 1. Two Sum
- Managed Server Independence
- centos升级openssh的两种方式
- c语言中数组名代表数组首地址,它的值在运行期间可以改变吗?
- 关于jQuery的$.getJSON乱码问题