Aisell-day04-登录与角色

1.登录功能

1.1数据库密码修改
1.1.1准备一个加密算法

//准备加密算法工具类
public class MD5Util {

    //设置盐值
    public static final String SALT = "itsource";

    //设置加密次数
    public static final int HASHITERATIONS = 10;

    //设置编码
    public static final String code = "MD5";

    //传入一个字符串,返回一个md5编码的值
    public static String createMd5Str(String str){

        
        SimpleHash simpleHash = new SimpleHash(code,str,SALT,HASHITERATIONS);

        return simpleHash.toString();
    }
}

1.1.2通过算法修改数据库密码

@Test
public void testUpdatePwd() throws Exception{
    List<Employee> list = employeeService.findAll();
    for (Employee employee : list) {
        employee.setPassword(MD5Util.createMd5Str(employee.getPassword()));
        employeeService.save(employee); //注:save是添加与修改
    }
}

2.1.3.添加员工时密码应该加密

@Override
public void save(Employee employee) {
    if(employee.getId()==null){
        //代表是添加功能(需要进入密码加密)
        employee.setPassword(MD5Util.createMd5Str(employee.getPassword()));
    }
    super.save(employee);
}

1.2 准备登录界面
login.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2019/10/9 0009
  Time: 下午 7:36
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>源码智销系统</title>
    <%@ include file="/WEB-INF/head.jsp"%>
    <script type="text/javascript">

        //登录提交
        function submitForm(){
            //提交表单到后台程序
            $("#loginForm").form('submit',{
                url:'/login',
                success:function(result){
                    //返回json字符串 {success:true,msg:'xxx'}
                    //$ jquery里面对象
                    result = $.parseJSON(result);
                    if(result.success){
                        //跳转主页
                        location.href = "/main";
                    }else{
                        $.messager.alert("温馨提示", result.msg, "info");
                    }
                }
            })

        }

        //回车登录 --看到就OK
        $(document.documentElement).on("keyup", function(event) {
            console.debug(event.keyCode);
            //获取按键
            var keyCode = event.keyCode;
            console.debug(keyCode);
            if(keyCode===13){
                submitForm();
            }
        });

        //重置表单
        function resetForm() {
            //清空登录表单
            $("#loginForm").form('clear')
        }

        //解决登录过期问题
        if(top!=window){
            //如果不是顶级窗口
            window.top.location.href = window.location.href;

        }


    </script>
</head>
<body>


<div align="center" style="margin-top: 100px;">
    <div class="easyui-panel" title="智销系统用户登陆" style="width: 350px; height: 240px;">
        <form id="loginForm" class="easyui-form" method="post">
            <table align="center" style="margin-top: 15px;">
                <tr height="20">
                    <td>用户名:</td>
                </tr>
                <tr height="10">
                    <td><input name="username" class="easyui-validatebox" required="true" value="admin" /></td>
                </tr>
                <tr height="20">
                    <td>&emsp;:</td>
                </tr>
                <tr height="10">
                    <td><input name="password" type="password" class="easyui-validatebox" required="true" value="0" /></td>
                </tr>
                <tr height="40">
                    <td align="center"><a href="javascript:;" class="easyui-linkbutton" onclick="submitForm();">登陆</a> <a
                            href="javascript:;" class="easyui-linkbutton" onclick="resetForm();">重置</a></td>
                </tr>
            </table>
        </form>
    </div>
</div>
</body>
</html>

2.2.2.修改LoginController

@Controller
public class LoginController {
    //用于完成跳转
    @RequestMapping(value="/login",method = RequestMethod.GET)
    public String index(){
        return "login";
    }
    //用于完成登录
    @RequestMapping(value="/login",method = RequestMethod.POST)
    public String login(String username,String password){

2.2.3.修改applicationContext-shiro.xml

<!-- 5.shiro的真实过滤器(注:这个名称必需和web.xml的代表过滤器【DelegatingFilterProxy】名称一样) -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager"/>
    <!-- 登录的url,如果没有登录,你访问的路径会跳到这个页面 -->
    <property name="loginUrl" value="/login"/>
    <!-- 登录成功的url,如果登录成功,会跳转到这个页面 -->
    <property name="successUrl" value="/main"/>
    <!-- 没有权限时跳转到这个位置 -->
    <property name="unauthorizedUrl" value="/s/unauthorized.jsp"/>
      <!-- 这个配置我们可以直接给一个map(动态的可以从代码中获取) -->
    <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
</bean>

2.2.4.设置静态资源设置放行

/**
 * 准备一个构造器类
 */
public class FilterChainDefinitionMapBuilder {

    public Map<String,String> createFilterChainDefinitionMap(){
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap();
        //注:对于一些不登录也可以放行的设置(大家可以根据实际情况添加)
        filterChainDefinitionMap.put("/login","anon");
        filterChainDefinitionMap.put("*.js","anon");
        filterChainDefinitionMap.put("*.css","anon");
        filterChainDefinitionMap.put("/css/**","anon");
        filterChainDefinitionMap.put("/js/**","anon");
        filterChainDefinitionMap.put("/easyui/**","anon");
        filterChainDefinitionMap.put("/images/**","anon");
        //这个值之后从数据库中查询到【用户-角色-权限-资源】
        //filterChainDefinitionMap.put("/s/permission.jsp","perms[user:*]");
        //filterChainDefinitionMap.put("/s/employee.jsp","perms[employee:*]");
        filterChainDefinitionMap.put("/**","authc");
        return  filterChainDefinitionMap;
    }
}

2.3.完成登录功能
2.3.1.EmployeeService:提供根据名称查询用户功能

@Override
public Employee findByUsername(String username) {
    return employeeRepository.findByUsername(username);
}

2.3.2.JpaRealm:数据库数据

public class JpaRealm extends AuthorizingRealm {

    @Autowired
    private IEmployeeService employeeService;

    。。。

    //AuthenticationInfo:认证; 身份验证; 证明
    //登录的时候就会调用这个方法来做验证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //身份认证(用户名)
        // 1.拿到用户名
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;
        String username = usernamePasswordToken.getUsername();
        // 2.根据用户名到数据库拿到用户
        Employee loginUser = employeeService.findByUsername(username);
        if(loginUser==null){
            return null; //该用户名不存在
        }
        //从数据库中拿到密码
        Object credentials = loginUser.getPassword();
        //加盐的数据
        ByteSource byteSource = ByteSource.Util.bytes("itsource");
        return new SimpleAuthenticationInfo(loginUser,credentials,byteSource,getName());
    }
}

2.3.3.JsonResult:返回数据封装

//封装对象,确定返回值
public class JsonResult {

    //是否操作成功
    private boolean success = true;
    //相应的提示信息
    private String msg;

    public JsonResult(){}
    public JsonResult(boolean success, String msg) {
        this.success = success;
        this.msg = msg;
    }
    。。。
}

2.3.4.LoginController:登录功能

@RequestMapping(value="/login",method = RequestMethod.POST)
@ResponseBody
public JsonResult login(String username, String password){ 
    //1.拿到访问的主体(当前登录用户)
    Subject subject = SecurityUtils.getSubject();
    //2.判断这个用户是否已经登录(通过验证)
    if(!subject.isAuthenticated()){
        //3.如果没有验证,就要完成登录
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
        try{
            //4.根据toke完成登录功能
            subject.login(token);
        }catch (UnknownAccountException e){
            System.out.println("用户名不存在!!");
            e.printStackTrace();
            return new JsonResult(false,"账号或者密码出错!");
        }catch (IncorrectCredentialsException e){
            System.out.println("密码不存在!");
            e.printStackTrace();
            return new JsonResult(false,"账号或者密码出错!");
        }catch (AuthenticationException e){
            System.out.println("登录出错!");
            e.printStackTrace();
            return new JsonResult(false,"程序发生未知错误!");
        }

    }
    return new JsonResult();
}

2.3.5.login.jsp:登录功能

function submitForm() {
    $("#loginForm").form("submit", {
        url : "/login",
        success : function(data) {
            data = $.parseJSON(data);
            if (data.success) {
                location.href = "/main";
            } else {
                $.messager.alert("温馨提示", data.msg, "info");
            }
        }
    });
}
function resetForm() {
    $("#loginForm").form("clear");
}

2.4.其它功能
2.4.1.回车登录(理解代码即可)

$(document.documentElement).on("keyup", function(event) {
    //console.debug(event.keyCode);
    var keyCode = event.keyCode;
    console.debug(keyCode);
    if (keyCode === 13) { // 捕获回车
        submitForm(); // 提交表单
    }
});

2.4.2.登录过期问题

// 检查自己是否是顶级页面
if (top != window) {// 如果不
是顶级
    //把子页面的地址,赋值给顶级页面显示
    window.top.location.href = window.location.href;
}

2.4.3.展示用户名与注销

</head>
<body class="easyui-layout">
<div data-options="region:'north',split:true" style="height:120px;">
    <h1 style="margin-bottom: 10px">源码时代智销系统</h1>
    <div style="text-align:right;margin-right: 10px">
        欢迎<font color="red"><shiro:principal /></font>进入
        <a href="/logout">注销</a>
    </div>
</div>
<div data-options="region:'west',title:'菜单',split:true" style="width:230px;">
    <ul id="menuTree"></ul>
</div>
<div id="dataTab" data-options="region:'center'"
     class="easyui-tabs" style="padding:5px;background:#eee;">
    <div title="Home">
    </div>
</div>
</body>
</html>

3.权限domain部分

3.1.为什么要做权限

为什么要做权限?对于我们系统的一些功能只有特定角色用户才能访问。要做权限判断。

资源/菜单:
系统中所有可以访问的功能(其实还是:url的访问地址也可以说是控制器的方法)
资源的格式:
自己实现拦截:
控制器的全类名:方法名
即:cn.itsource.crm.web.controller.EmployeeController.list
Shiro拦截:
Url:/department/add

权限:简单理解就是给资源加锁
针对资源,代表对某个资源的功能添加了控制,就是需要控制权限(即上锁)。
针对用户,代表用户可以使用某个被控制的功能(即钥匙,钥匙是通过角色获取的)。

角色:针对权限,即权限的打包,就是权限组。
用户:访问系统的人。
在这里插入图片描述3.2.1.权限和资源之间的一对一
一个权限只用来控制一个资源。
CREATE TABLE t_resource (
id bigint(20) NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
url varchar(255) NOT NULL, 如果是自己拦截换成Controller.方法名,shiro不变
status int(11) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE t_permission (
id bigint(20) NOT NULL AUTO_INCREMENT,
name varchar(40) NOT NULL,
resourceId
resource varchar(80) NOT NULL,
PRIMARY KEY (id),
KEY id (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
在权限表中添加资源ID外键字段。变形在权限中添加一个资源的字段,不要资源表。
在这里插入图片描述3.2.2.权限与资源之间的多对多

一个权限控制器多个资源,一个资源属于多个权限。
列表部门权限-----列表部门资源,列表员工资源
列表员工资源—列表员工权限,列表部门权限

在这里插入图片描述3.3.创建Domain
3.3.1.Employee

@Entity
@Table(name="employee")
public class Employee extends BaseDomain {//多对多:配置中间表
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "employee_role",
            joinColumns = @JoinColumn(name = "employee_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id")
    )
   @JsonIgnore //生成json是忽略这个属性(数据大多,全部拿到没有意义,还有可能造成死循环)
    private Set<Role> roles = new HashSet<>();
}

3.3.2.Role

@Entity
@Table(name="role")
public class Role extends BaseDomain {

    private String name; //角色名称
    private String sn; //角色编码
    //角色对应权限
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "role_permission",
            joinColumns = @JoinColumn(name = "role_id"),
            inverseJoinColumns = @JoinColumn(name = "permission_id")
    )
     private List<Permission> permissions = new ArrayList<>();}

3.3.3.Permission

@Entity
@Table(name="permission")
public class Permission extends BaseDomain {
    private String name;
    private String url; //对应的资源
    private String descs;
    private String sn; //对象的权限名称.
}

3.3.4.完善权限页面

<table id="permissionGrid">
    <thead>
    <tr>
        <th width="20"  field="name" >名称</th>
        <th width="20"  field="url" >对应的资源</th>
        <th width="20"  field="sn" >对象的权限</th>
        <th width="20"  field="descs" >描述</th>
    </tr>
    </thead>
</table>

<div id="permissionDialog" class="easyui-dialog" data-options="closed:true,modal:true" title="功能操作" style="width:400px">
    <div style="padding:10px 60px 20px 40px">
        <form id="permissionForm" class="easyui-form" method="post" data-options="">
            <input type="hidden" id="permissionId" name="id" >
            <table cellpadding="5">
                <tr>
                    <td>名称:</td>
                    <td><input class="easyui-validatebox" name="name" data-options="required:true"></input></td>
                </tr>
                <tr>
                    <td>资源路径:</td>
                    <td><input class="easyui-validatebox" type="text" name="url" data-options="required:true"></input></td>
                </tr>
                <tr>
                    <td>权限:</td>
                    <td><input class="easyui-validatebox" type="text" name="sn" data-options="required:true"></input></td>
                </tr>
                <tr>
                    <td>描述:</td>
                    <td><input class="easyui-validatebox" type="text" name="descs"></input></td>
                </tr>
            </table>
        </form></div>

4.角色管理(页面功能)

在这里插入图片描述4.1.准备角色数据显示
4.1.1.role.jsp

<table id="roleGrid" class="easyui-datagrid" data-options="fit:true,fixed:true,fitColumns:true,toolbar:'#tb',singleSelect:true";
       url="/role/page"
       iconCls="icon-save"
       rownumbers="true" pagination="true">
    <thead>
    <tr>
        <th width="20"  field="name" >名称</th>
        <th width="20"  field="sn" >编码</th>
        <th width="20"  field="permissions" data-options="formatter:formatperms">权限</th>
    </tr>
    </thead>
</table>

4.1.2.role.js

function formatperms(val){
    var persStr = "" ;
    for(var i=0;i<val.length;i++){
        var permission = val[i];
        persStr += permission.name;
        if(i<val.length-1){
            persStr+=",";
        }
    }
    return persStr;
}

4.2.添加修改角色

<div id="roleDialog" class="easyui-dialog" data-options="closed:true,modal:true" title="功能操作" style="width:900px">
    <div style="padding:10px 60px 20px 40px">
        <form id="roleForm" class="easyui-form" method="post" data-options="">
            <input type="hidden" id="roleId" name="id" >
            <table cellpadding="5" style="width: 100%;">
                <tr>
                    <td>
                        名称:<input class="easyui-validatebox" type="text" name="name" data-options="required:true"></input>
                        编码:<input class="easyui-validatebox" type="text" name="sn" data-options="required:true"></input>
                    </td>
                </tr>
                <tr>
                   <td colspan="2">
                       <div id="cc" class="easyui-layout" style="width: 100%;height:400px;">
                           <div data-options="region:'west',split:true" style="width:50%;">
                               <table id="userPermissionGrid">
                                   <thead>
                                   <tr>
                                       <th width="10"  field="name" >名称</th>
                                       <th width="20"  field="url" >对应的资源</th>
                                       <th width="20"  field="sn" >对象的权限</th>
                                   </tr>
                                   </thead>
                               </table>
                           </div>
                           <div data-options="region:'center'" style="padding:5px;background:#eee;">
                               <table id="allPermissionGrid">
                                   <thead>
                                   <tr>
                                       <th width="10"  field="name" >名称</th>
                                       <th width="20"  field="url" >对应的资源</th>
                                       <th width="20"  field="sn" >对象的权限</th>
                                   </tr>
                                   </thead>
                               </table>
                           </div>
                       </div>
                   </td>
                </tr>

            </table>
        </form>
        <div style="text-align:center;padding:5px">
            <a href="javascript:void(0)" class="easyui-linkbutton" data-method="save">提交</a>
            <a href="javascript:void(0)" class="easyui-linkbutton" onclick="$('#roleDialog').dialog('close')">取消</a>
        </div>
    </div>
</div>

role.js

//用户权限列表
var userPermissionGrid =$("#userPermissionGrid");
//所有权限列表
var allPermissionGrid =$("#allPermissionGrid");

…
var itsource={//添加数据(弹出添加的模态框)
    add:function () {
        //如果有data-save属性,我们把它展示出来
        $("*[data-save]").show();
        //启动有data-save属性的input元素的验证功能
        $("*[data-save] input").validatebox("enableValidation");
        roleForm.form("clear");//清除数据
        //弹出表单窗口
        roleDialog.dialog("center").dialog('open');
        //当前用户权限清空
        userPermissionGrid.datagrid("loadData",[]);
    },
    //修改数据(弹出修改的模态框)
    edit:function () {
        //选中了某一条数据才删除
        var row = roleGrid.datagrid("getSelected");
        if(row){
            //隐藏有data-save属性的元素
            $("*[data-save]").hide();
            //禁用有data-save属性的input元素的验证功能
            $("*[data-save] input").validatebox("disableValidation");

            roleForm.form("clear");//清除数据
            roleDialog.dialog("center").dialog('open');
            //为form加载数据
            roleForm.form("load",row);

            //单独解决权限的回显问题(注意,这里要准备一个新的权限,以免删除时出问题)
            var permissions = [];
            $.extend(permissions,row.permissions);
            userPermissionGrid.datagrid("loadData",permissions);

        }else{
            $.messager.alert('提示信息','请选择一行再进行修改!','info');
        }
    },//双击所有权限,把一个权限交给这个角色
    addPermission:function(rowIndex, rowData){
        //判断是否有重复的行
        //1.拿到角色权限所有的行数据
        var rows = userPermissionGrid.datagrid("getRows");
        //2.遍历rows拿到每一个员工数据
        for(var i=0;i<rows.length;i++){
            var row = rows[i];                //3.判断如果出现相等数据的情况

            if(row.id == rowData.id){
                //不做任何操作(也可以给出提示)
                $.messager.show({
                    title:'提示',
                    msg:'该权限已经添加!',
                    timeout:2000,
                    showType:'slide'
                });
                return;
            }
        }
        //把数据加到相应角色权限Grid中
        userPermissionGrid.datagrid("appendRow",rowData);
    },
    //双击后移动相应的权限
    removePermission:function (rowIndex, rowData) {
        userPermissionGrid.datagrid("deleteRow",rowIndex);
    }
}

//我们在这里创建两个Grid
//1.创建当前角色的权限Grid
userPermissionGrid.datagrid({
    fit:true, //自适应
    fixed:true,
    fitColumns:true,
    //双击后移除权限
    onDblClickRow:itsource.removePermission
})

//2.获取所有权限的Grid
allPermissionGrid.datagrid({
    fit:true,
    fixed:true,
    fitColumns:true,
    //toolbar:'#tb',
    // singleSelect:true";
    url:"/permission/page",
    rownumbers:"true" ,
    pagination:"true",
    //双击后添加权限
    onDblClickRow:itsource.addPermission
})

4.3.保存数据

var itsource={
    //保存数据
    save:function () {
        var url  = "/role/save";
        var id = $("#roleId").val();
        if(id){
            url = "/role/update?cmd=update";
        }
        roleForm.form('submit', {
            url:url,
            //这里我们加上params,可以通过这个参数修改咱们的提交数据
            onSubmit: function(param){
                //得到所有明细
                var rows = userPermissionGrid.datagrid("getRows");
                //准备传参数据(权限多条数据,是个数组)
                //param.permissions = [];
                for(var i=0;i<rows.length;i++){
                    //设置相应的数据值
                    param["permissions["+i+"].id"] =rows[i].id;
                }

                //做验证
                return roleForm.form("validate");
            },
            success:function(data){
                var result = JSON.parse(data);//转成相应的json数据
                if(result.success) {
                    roleGrid.datagrid('reload');
                    roleDialog.dialog('close');
                }else{
                    $.messager.alert('提示信息','操作失败!,原因:'+result.msg,"error");
                }
            }
        })
    }
   }
/**
 * 特性:在执行相应方法之前都会先执行这个方法
 */
@ModelAttribute("editRole")
public Role beforeEdit(Long id, String cmd){
    //有id的时候-> 修改功能
    if(id!=null && "update".equals(cmd)) {
        Role role = roleService.findOne(id);
        //把这个要修改的关联对象设置为null,可以解决n-to-n的问题
        role.getPermissions().clear();
        return role;
    }
    return null;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值