四、编写MySQLManager类
这个类处理插件希望传给MySQL的各种命令,配合SQLCommand生成MySQL命令提交给MySQL执行,如果是查询的话同时返回查询结果。
我们需要先写get方法。get方法主要是返回一个MySQLManager类的实例,可以帮助我们静态引用非静态方法。首先我们可以先声明一个叫做instance的字段。设置为null,之后编写get方法。由于如果onCommand方法或者onEnable方法在调用instance的时候是null会抛出空指针异常。那么我们就可以编写方法了。
之后,我们编写一个很重要的方法,即初始化数据库的方法enableMySQL。
首先,声明一下前面提到的“五要素”,而这个方法利用写在主类里面的getConfigString和getConfigInt方法,获取配置文件的设置,通过调用connectMySQL方法连接上数据库,顺便创建一个叫做table1的表,这个创建的过程是这样的。
我不会美工啊别吐槽这图
而connectMySQL方法,主要就是发送连接数据库指令,格式为
jdbc:mysql://IP:端口/数据库?参数1=值1&参数2=值2
用户名和密码单独放在后面
Connection类型的connection是一个连接对象,在通过DriverManager.getConnection方法获取之后就可以代表一个连接。
doCommand方法需要一个PreparedStatement类型的字段为参数,调用ps.executeUpdate();即可发送给MySQL执行了。
至于为什么要新写一个doCommand方法,而不在下面直接executeUpdate,我们马上说。
注意:由于在编写代码时使用了异步(多线程)操作,也就是说connection在执行任务时服务器不需要等待其执行完毕(如果等待了就卡服了),那么会出现一个问题:
在多线程环境中,线程A正在doCommand,还没有结束的时候线程B却意外的申请doCommand,这个情况该怎么办。
换句话说,如果你执行/ms add 12 data1,在没有执行完的时候又以极快的速度执行了/ms add 13 data2,那么就很尴尬。
有一个解决办法是在doCommand加上synchronized关键字,表示给方法上锁(使得执行同步化),这就是为什么我们要新写一个doCommand方法的原因。因为加上之后如果出现这种情况,让第二个操作等待之,直到doCommand执行完之后再执行。当然这个方法在执行大量SQL语句的时候效率降低了很多。
另一个解决办法的使用连接池技术。这个技术将在下文附录1介绍。
之后,我们要写shutdown方法connection.close();指令表示关闭数据库连接对象。
现在,我们进入重点:实现对数据库的增删查
增加:编写insertData方法,用于添加一个int类型的数据和一个String类型的数据
上文提到,?是通配符,使用PreparedStatement可以防止SQL注入式攻击,原理在附录3中讲解。
那么,我们需要替换之。我们可以先这样写:
- public void insertData(String data1, String data2, CommandSender sender) {
- try {
- PreparedStatement ps;
- String s = SQLCommand.ADD_DATA.commandToString();
- ps = connection.prepareStatement(s);
- ps.setInt(1, Integer.parseInt(data1));
- ps.setString(2, data2);
- doCommand(ps, sender);
- } catch (SQLException e) {
- e.printStackTrace();
- } catch (NumberFormatException e) {
- sender.sendMessage("输入的不是整数,插入失败");
- }
- }
复制代码
这样的话,我们可以将通配符?替换掉了,而且由于已经预编译了,在一定程度上防止了注入攻击。
删除差不多,由于我们设置了int键为主键,所以我们可以照葫芦画瓢,轻轻松松写出删除的代码
- public void deleteData(String data1, CommandSender sender) {
- try {
- PreparedStatement ps;
- String s = SQLCommand.DELETE_DATA.commandToString();
- ps = connection.prepareStatement(s);
- ps.setInt(1, Integer.parseInt(data1));
- doCommand(ps, sender);
- } catch (SQLException e) {
- e.printStackTrace();
- } catch (NumberFormatException e) {
- sender.sendMessage("输入的不是整数,删除失败");
- }
- }
复制代码而查询,这个是数据库操作指令中使用得最频繁最多的指令之一。为此我们先大致介绍一下用法,更高级使用请学习附录2的“实现异步回调,并且获取结果”部分。
由于我们要获取结果,所以我们需要调用executeQuery方法。这个方法返回一个ResultSet类型的结果集,而我们可以遍历这个集合获得结果。
- public void findData(String data1, CommandSender sender) {
- try {
- String s = SQLCommand.SELECT_DATA.commandToString();
- PreparedStatement ps = connection.prepareStatement(s);
-
- ps.setInt(1, Integer.parseInt(data1));
- ResultSet rs = ps.executeQuery();
- while (rs.next())
- {
- String str = rs.getString("string");
- sender.sendMessage(str);
- }
- } catch (SQLException e) {
- // TODO 自动生成的 catch 块
- sender.sendMessage("查询失败");
- } catch (NumberFormatException e) {
- sender.sendMessage("输入的不是整数,查询失败");
- }
- }
复制代码至此,我们已经学习完毕所有关于MySQL的
最基本的内容了,编写一款MySQL插件应该不难了。
不过,我还是推荐大家学习一下附录的内容,这部分内容不是必须掌握的,但是可以帮助理解上文提到的部分问题。
这个类的最终代码: