1 前言&概述

这篇文章是基于此处文章的更新,更新了一些技术栈,更加贴近实际需要,以及修复了若干的错误。

这是一个前端Android+后端Java/Kotlin通过Servelt进行后台数据库(MySQL)交互的详细步骤以及源码实现,技术栈:

  • Android基础
  • 原生JDBC+原生Servlet
  • Tomcat+MySQLDocker

当然现在的很多Java后端开发都使用了Spring Boot而不是原生的Servlet,所以使用Spring Boot实现的可以笔者的另一篇文章

尽管基于Spring Boot实现非常的简便,但是使用原生的Servlet更能理解底层的原理。另外本篇文章是偏基础向的教程,很多步骤都会比较详细而且附上了图,好了废话不说,正文开始。

2 环境

  • Android Studio 4.1.2
  • IntelliJ IDEA 2020.3
  • MySQL 8.0.23
  • Tomcat 10.0
  • Docker 20.10.1
  • 服务器CentOS 8.1.1911

3 环境准备

3.1 IDE准备

官网安装Android Studio+IDEA,这部分就省略了。

3.2 MySQL

3.2.1 安装概述

这里的MySQL若无特殊说明指的是MySQL Community

首先,在Windows下,MySQL提供了exe安装包:

macOS下提供了dmg安装包:

可以戳这里下载。

Linux下一般来说MySQL安装有如下方式:

  • 软件包安装(apt/apt-getyumdnfpacman等)
  • 下载压缩包安装
  • 源码编译安装
  • Docker安装

其中相对省事的安装方式为Docker安装以及软件包安装,其次是压缩包方式安装,特别不建议源码安装(当然如果喜欢挑战的话可以参考笔者的一篇编译安装8.0.19以及编译安装8.0.20)。

3.2.2 安装开始

这里笔者本地测试选择的是使用Docker安装,步骤可以查看这里

另外对于服务器,也可以使用Docker安装,如果使用软件包安装的话,这里以笔者的CentOS8为例,其他系统的参考如下:

3.2.2.1 下载并安装

添加仓库:

sudo yum install https://repo.mysql.com/mysql80-community-release-el8-1.noarch.rpm

禁用默认MySQL模块(CentOS8中会包含一个默认的MySQL模块,不禁用的话没办法使用上面添加的仓库安装):

sudo yum module disable mysql

安装:

sudo yum install mysql-community-server

3.2.2.2 启动服务并查看初始化密码

启动服务:

systemctl start mysqld

查看临时密码:

sudo grep 'temporary password' /var/log/mysqld.log

输入临时密码登录:

mysql -u root -p

修改密码:

alter user 'root'@'localhost' identified by 'PASSWORD'

3.2.2.3 创建外部访问用户

不建议在Java中直接访问root用户,一般是新建一个对应权限的用户并进行访问,这里就为了方便就省略了。

3.3 Tomcat

3.3.1 本地Tomcat

Tomcat安装不难,直接从官网下载即可:

解压:

tar -zxvf apache-tomcat-10.0.0.tar.gz

进入bin目录运行startup.sh

cd apache-tomcat-10.0.0/bin
./startup.sh

本地访问localhost:8080

这样就算成功了。对于Windows的读者,可以戳这里下载,解压步骤类似,解压后运行startup.bat即可访问localhost:8080

3.3.2 服务器Tomcat

服务器的话可以直接使用wget安装:

wget https://downloads.apache.org/tomcat/tomcat-10/v10.0.0/bin/apache-tomcat-10.0.0.tar.gz

但是这样速度很慢,建议下载到本地再使用scp上传:

scp apache-tomcat-10.0.0.tar.gz username@xxx.xxx.xxx.xxx:/

一样按照上面的方法解压后运行startup.sh,访问公网IP:8080即可观察是否成功。

4 建库建表

4.1 用户表

这里使用到的MySQL脚本如下:

CREATE DATABASE userinfo;
USE userinfo;
CREATE TABLE user
(
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name CHAR(30) NULL,
password CHAR(30) NULL
)

4.2 导入

mysql -u root -p < user.sql

5 后端部分

因为是比较基础向的教程,所以先从创建项目开始吧。

5.1 创建项目+导库

选择对应Java Enterprise,默认是选中了其中的Web application,构建工具默认Maven,测试工具JUnit,如果需要GradleKotlin的话自行勾选即可:

2020.3版本的IDEA相比起以前,更加人性化的添加了选择库的功能,默认是选中了Servlet,需要其他库的话自行选择即可。

另外一个要注意的是JavaEE已经更名为JakartaEE,因此版本这里可以选择JakartaEE

填上对应包名并选择位置:

创建完成后,这里笔者遇到了一个错误,找不到对应的Servlet包:

在设置中选择更新中心仓库即可:

创建后的目录如图所示:

接着添加依赖,用到的依赖包括:

  • MySQL
  • Jackson
  • Lombok

添加到pom.xml中即可(注意版本,MySQL不同版本可以查看这里):

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>

这样第一步就完成了。

5.2 结构

项目结构如下:

  • 持久层操作:Dao
  • 实体类:User
  • 响应体:ResponseBody
  • Servlet层:SignIn/SignUp/Test
  • 工具类:DBUtils
  • 启动类:不需要,因为在Web服务器中运行

先创建好文件以及目录:

5.3 DBUtils

原生JDBC获取连接工具类:

package com.example.javawebdemo.utils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException; public class DBUtils {
private static Connection connection = null; public static Connection getConnection() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
final String url = "jdbc:mysql://127.0.0.1:3306/userinfo";
final String username = "root";
final String password = "123456";
connection = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
return null;
}
return connection;
} public static void closeConnection() {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

重点在这四行:

Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://127.0.0.1:3306/userinfo";
String username = "root";
String password = "123456";

根据个人需要修改,注意MySQL8注册驱动与旧版的区别,旧版的是:

Class.forName("com.mysql.jdbc.Driver");

5.4 User

三字段+@Getter

package com.example.javawebdemo.entity;

import lombok.Getter;

@Getter
public class User {
private final String name;
private final String password; public User(String name, String password) {
this.name = name;
this.password = password;
}
}

5.5 Dao

数据库操作层:

package com.example.javawebdemo.dao;

import com.example.javawebdemo.entity.User;
import com.example.javawebdemo.utils.DBUtils; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; public class Dao {
public boolean select(User user) {
final Connection connection = DBUtils.getConnection();
final String sql = "select * from user where name = ? and password = ?";
try {
final PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, user.getName());
preparedStatement.setString(2, user.getPassword());
ResultSet resultSet = preparedStatement.executeQuery();
return resultSet.next();
} catch (SQLException e) {
e.printStackTrace();
return false;
} finally {
DBUtils.closeConnection();
}
} public boolean insert(User user) {
final Connection connection = DBUtils.getConnection();
final String sql = "insert into user(name,password) values(?,?)";
try {
final PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, user.getName());
preparedStatement.setString(2, user.getPassword());
preparedStatement.executeUpdate();
return preparedStatement.getUpdateCount() != 0;
} catch (SQLException e) {
e.printStackTrace();
return false;
} finally {
DBUtils.closeConnection();
}
}
}

两个操作:

  • 查询:存在该用户返回true,否则false
  • 插入:添加用户

注意插入操作中使用executeUpdate()进行插入,同时使用getUpdateCount() != 0判断插入的结果,而不能直接使用

return preparedStatement.execute();

一般来说:

  • selectexecuteQuery()executeQuery()返回ResultSet,表示结果集,保存了select语句的执行结果,配合next()使用
  • delete/insert/update:使用executeUpdate()executeUpdate()返回的是一个整数,表示受影响的行数,即delete/insert/update修改的行数,对于drop/create操作返回0
  • create/drop:使用execute()execute()的返回值是这样的,如果第一个结果是ResultSet对象,则返回true,如果第一个结果是更新计数或者没有结果则返回false

所以在这个例子中

return preparedStatement.execute();

肯定返回false,不能直接判断是否插入成功。

5.6 响应体

添加一个响应体类方便设置返回码以及数据:

package com.example.javawebdemo.response;

import lombok.Getter;
import lombok.Setter; @Setter
@Getter
public class ResponseBody{
private Object data;
private int code;
}

5.7 Servlet

  • SingIn类用于处理登录,调用JDBC查看数据库是否有对应的用户
  • SignUp类用于处理注册,把User添加到数据库中
  • Test为测试Servlet,返回固定字符串

先上SignIn.java

package com.example.javawebdemo.servlet;

import com.example.javawebdemo.dao.Dao;
import com.example.javawebdemo.entity.User;
import com.example.javawebdemo.response.ResponseBody;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/sign/in")
public class SignIn extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("application/json;charset=utf-8"); String name = req.getParameter("name");
String password = req.getParameter("password"); Dao dao = new Dao();
User user = new User(name,password);
ObjectMapper mapper = new ObjectMapper();
ResponseBody body = new ResponseBody(); if (dao.select(user)) {
body.setCode(200);
body.setData("success");
} else {
body.setCode(404);
body.setData("failed");
}
mapper.writeValue(resp.getWriter(), body);
}
}

注意点:

  • @WebServlet:定义Servlet(不加这个注解也是可以的但是需要在web.xml中手工定义Servlet),默认的属性为value,表示Servlet路径
  • 编码:HttpServletRequest/HttpServletResponse均设置UTF8(虽然在这个例子中并不是必要的因为没有中文字符)
  • 获取参数:request.getParameter,从请求中获取参数,传入的参数是键值
  • 写响应体:利用Jackson,将response.getWriter以及响应体传入,接着交给mapper.writeValue进行写响应体

下面是SignUp.java,大部分代码类似:

package com.example.javawebdemo.servlet;

import com.example.javawebdemo.dao.Dao;
import com.example.javawebdemo.entity.User;
import com.example.javawebdemo.response.ResponseBody;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/sign/up")
public class SignUp extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("application/json;charset=utf-8"); String name = req.getParameter("name");
String password = req.getParameter("password");
Dao dao = new Dao();
User user = new User(name,password);
ResponseBody body = new ResponseBody();
ObjectMapper mapper = new ObjectMapper();
if (dao.insert(user)) {
body.setCode(200);
body.setData("success");
} else {
body.setCode(500);
body.setData("failed");
}
mapper.writeValue(resp.getWriter(), body);
}
}

测试Servlet

package com.example.javawebdemo.servlet;

import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/test")
public class Test extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.getWriter().print("Hello, Java Web");
}
}

5.8 运行

需要借助Tomcat运行,选择运行配置中的Tomcat Server

设置Tomcat根目录:

接着在Deployment选择+后,选择第二个带exploded的(当然第一个也不是不可以,不过第一个一般是发布到远程版本,是以WAR形式的,而第二个是直接将所有文件以当前目录形式复制到webapps下,并且在调试模式下支持热部署):

另外可以把这个路径修改为一个比较简单的路径,方便操作:

调试(运行不能进行热部署):

访问localhost:8080/demoIDEA应该会自动打开)会出现如下页面:

访问路径下的test会出现:

这样后端就处理完成了,下面处理Android端。

6 Android

6.1 新建项目

6.2 依赖/权限

依赖如下:

implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.12.1'

build.gradle中加上即可,另外,再加上:

buildFeatures{
viewBinding = true
}

viewBinding就是视图绑定功能,以前是通过findViewById获取对应的组件,后面就有了Butter Knife,到现在Butter Knife过期了,推荐使用view binding

另外在AndroidManifest.xml中加入网络权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

还需要添加HTTP的支持,因为这是一个示例Demo就不上HTTPS了,但是目前Android的版本默认不支持,因此需要在<application>添加:

android:usesCleartextTraffic="true"

6.3 项目结构

四个文件:

  • MainActivity:核心Activity
  • NetworkSettings:请求URL,常量
  • NetworkThread:网络请求线程
  • ResponseBody:请求体

6.4 ResponseBody

package com.example.androiddemo;

public class ResponseBody {
private int code;
private Object data; public int getCode() {
return code;
} public Object getData() {
return data;
}
}

响应体,一个返回码字段+一个数据字段。

6.5 NetworkSettings

package com.example.androiddemo;

public class NetworkSettings {
private static final String HOST = "192.168.43.35";
private static final String PORT = "8080";
public static final String SIGN_IN = "http://"+ HOST +":"+PORT + "/demo/sign/in";
public static final String SIGN_UP = "http://"+ HOST +":"+PORT + "/demo/sign/up";
}

请求URL常量,HOST请修改为自己的内网IP注意不能使用localhost/127.0.0.1

可以使用ip addr/ifconfig/ipconfig等查看自己的内网IP

6.6 NetworkThread

package com.example.androiddemo;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Callable; public class NetworkThread implements Callable<String> {
private final String name;
private final String password;
private final String url; public NetworkThread(String name, String password, String url) {
this.name = name;
this.password = password;
this.url = url;
} @Override
public String call(){
try {
//开启连接
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
//拼接数据
String data = "name="+ URLEncoder.encode(name, StandardCharsets.UTF_8.toString())+"&password="+URLEncoder.encode(password,StandardCharsets.UTF_8.toString());
//设置请求方法
connection.setRequestMethod("POST");
//允许输入输出
connection.setDoInput(true);
connection.setDoOutput(true);
//写数据(也就是发送数据)
connection.getOutputStream().write(data.getBytes(StandardCharsets.UTF_8));
byte [] bytes = new byte[1024];
//获取返回的数据
int len = connection.getInputStream().read(bytes);
return new String(bytes,0,len,StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}

发送网络请求的线程类,由于是异步操作的线程,实现了Callable<String>接口,表示返回的是String类型的数据,主线程可通过get()阻塞获取返回值。

6.7 MainActivity

package com.example.androiddemo;

import android.os.Bundle;
import android.view.View;
import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.example.androiddemo.databinding.ActivityMainBinding;
import com.fasterxml.jackson.databind.ObjectMapper; import java.util.concurrent.FutureTask; public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding;
private final ObjectMapper mapper = new ObjectMapper(); @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
} public void signIn(View view){
String name = binding.editTextName.getText().toString();
String password = binding.editTextPassword.getText().toString();
FutureTask<String> signInTask = new FutureTask<>(new NetworkThread(name,password,NetworkSettings.SIGN_IN));
Thread thread = new Thread(signInTask);
thread.start();
try{
//get获取线程返回值,通过ObjectMapper反序列化为ResponseBody
ResponseBody body = mapper.readValue(signInTask.get(),ResponseBody.class);
//根据返回码确定提示信息
Toast.makeText(getApplicationContext(),body.getCode() == 200 ? "登录成功" : "登录失败",Toast.LENGTH_SHORT).show();
}catch (Exception e){
e.printStackTrace();
}
} public void signUp(View view){
String name = binding.editTextName.getText().toString();
String password = binding.editTextPassword.getText().toString();
FutureTask<String> signUpTask = new FutureTask<>(new NetworkThread(name,password,NetworkSettings.SIGN_UP));
Thread thread = new Thread(signUpTask);
thread.start();
try{
ResponseBody body = mapper.readValue(signUpTask.get(),ResponseBody.class);
Toast.makeText(getApplicationContext(),body.getCode() == 200 ? "注册成功" : "注册失败",Toast.LENGTH_SHORT).show();
}catch (Exception e){
e.printStackTrace();
}
}
}

说一下viewBinding,在onCreate中:

super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());

通过ActivityMainBinding的静态方法获取binding,注意ActivityMainBinding这个类的类名不是固定的,比如Android官方的文档中就是:

6.8 资源文件

两个:

  • activity_main.xml
  • strings.xml

分别如下,不细说了:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"> <TextView
android:id="@+id/textViewName"
android:layout_width="45dp"
android:layout_height="38dp"
android:layout_marginStart="24dp"
android:layout_marginTop="92dp"
android:text="@string/name"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" /> <EditText
android:id="@+id/editTextName"
android:layout_width="300dp"
android:layout_height="40dp"
android:layout_marginStart="64dp"
android:layout_marginTop="84dp"
android:autofillHints=""
android:inputType="text"
app:layout_constraintLeft_toLeftOf="@id/textViewName"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="LabelFor" /> <TextView
android:id="@+id/textViewPassword"
android:layout_width="45dp"
android:layout_height="36dp"
android:layout_marginStart="24dp"
android:layout_marginTop="72dp"
android:text="@string/password"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="@id/textViewName" /> <EditText
android:id="@+id/editTextPassword"
android:layout_width="300dp"
android:layout_height="40dp"
android:layout_marginStart="64dp"
android:layout_marginTop="72dp"
android:autofillHints=""
android:inputType="textPassword"
app:layout_constraintLeft_toLeftOf="@id/textViewPassword"
app:layout_constraintTop_toTopOf="@id/editTextName"
tools:ignore="LabelFor" /> <Button
android:id="@+id/buttonSignUp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="56dp"
android:layout_marginTop="32dp"
android:onClick="signUp"
android:text="@string/signUp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewPassword"
tools:ignore="ButtonStyle" /> <Button
android:id="@+id/buttonSignIn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="36dp"
android:layout_marginEnd="52dp"
android:onClick="signIn"
android:text="@string/signIn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editTextPassword"
tools:ignore="ButtonStyle" />
</androidx.constraintlayout.widget.ConstraintLayout>
<resources>
<string name="app_name">AndroidDemo</string>
<string name="name">用户名</string>
<string name="password">密码</string>
<string name="signUp">注册</string>
<string name="signIn">登录</string>
</resources>

7 测试

7.1 本地测试

首先运行Java Web端,应该会自动打开如下界面:

附加test后:

运行Android端,先输入一个不存在的用户名或密码,提示登录失败,再进行注册,然后登录成功:

同时查看后端数据库如下:

7.2 部署测试

首先确保本地数据库的用户名与密码与服务器的用户名与密码一致。同时存在对应的表以及库

部署Java Web端之前先在pom.xml中加入一个<finalName>

在右侧的工具栏先选择clean,再选择编译,最后选择打包:

之所以这样做是因为如果更新了文件,打包不会把文件更新再打包进去,因此需要先清除原来的字节码文件,再编译最后打包。

完成后会出现一个demo.war位于target下:

scp(或其他工具)上传到服务器,并移动到Tomcatwebapps(为了方便说明以下假设服务器的IP8.8.8.8):

scp demo.war 8.8.8.8/xxx
# 通过ssh连接服务器后
cp demo.war /usr/local/tomcat/webapps

启动Tomcat

cd /usr/local/tomcat/bin
./startup.sh

启动后就可以看见在webapps下多了一个demo的文件夹:

访问8.8.8.8/demo看到本地测试的页面就可以了。接着修改Android端的NetworkSettings中的HOST8.8.8.8,如果没问题的话就能正常访问了:

服务器数据库:

8 注意事项

注意事项比较琐碎而且有点多,因此另开了一篇博客,戳这里

如果还有其他问题欢迎留言。

9 源码

提供了Java+Kotlin两种语言实现:

如果觉得文章好看,欢迎点赞。

同时欢迎关注微信公众号:氷泠之路。

Android+Java Web+MySQL实现登录注册的更多相关文章

  1. java web 简单的登录注册

    --sql文件 create database studentgouse studentgocreate table stuinfo(--stuid int primary key identity( ...

  2. IOS, Android, Java Web Rest : RSA 加密和解密问题

    IOS, Android, Java Web Rest :  RSA 加密和解密问题 一对公钥私钥可以使用 OpenSSL创建, 通常 1024位长度够了. 注意: 1. 公钥私钥是BASE64编码的 ...

  3. java web SSO单点登录

    第一篇: Web应用系统的演化总是从简单到复杂,从单功能到多功能模块再到多子系统方向发展. .当前的大中型Web互联网应用基本都是多系统组成的应用群,由多个web系统协同为用户提供服务. 多系统应用群 ...

  4. Java Web实现用户登录界面

    一.学习Java Web需要的技术: Java语言基础:算法基础.常用数据结构.编程规范. 掌握常见的数据结构和实用算法:培养良好的编程习惯. Java面向对象:封装.继承.多态等,面向对象程序设计, ...

  5. Java Web实现用户登录功能

    java web 学习记录一下 mvc结构实现mysql 连接 什么是mvc MVC是模型(model).视图(view).控制(controller)这三个单词上的首字母组成.它是一种应用模型,它的 ...

  6. IDEA+MySQL实现登录注册的注册验证时出现 Cannot resolve query parameter '2'

    问题描述: 在IDEA+MySQL+Tomcat 实现登录注册JSP的注册信息INSERT验证时出现 Cannot resolve query parameter '2' 贴上创建链接的代码: if( ...

  7. 【知了堂学习笔记】java web 简单的登录

    最近皮皮潇在学习java web,刚接触了简单的东西,所以今天给大家带来一个简单的登录实现. 页面: 页面代码: <%@ page language="java" conte ...

  8. Express+MySQL实现登录注册的demo

    MySQL5.7.20 demo准备 安装MySQL,安装完毕之后添加系统环境变量在cmd中启动服务:net start mysql57,如果是安装MySQL8.0则服务名默认时mysql80,测试安 ...

  9. JAVA的SSH框架登录注册

    Struts 的MVC设计模式可以使我们的逻辑变得很清晰,主要负责表示层的显示. Spring 的IOC和AOP可以使我们的项目在最大限度上解藕. hibernate的就是实体对象的持久化了, 数据库 ...

随机推荐

  1. C++算法代码——纪念品分组[NOIP2007 普及组]

    题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1099 https://www.luogu.com.cn/problem/P1094 ...

  2. IO多路复用之select、poll、epoll

    本文转载自IO多路复用之select.poll.epoll 导语 IO多路复用:通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作. ...

  3. vue版本一直是2.9.6版本,卸载也卸载不掉,更新也更新不了

    原文链接:https://blog.csdn.net/zlzbt/article/details/110136755 主要是找到本地文件 E:\StudyFile\VueStudy λ where v ...

  4. Vue学习笔记-vue调试工具vue-devtools安装及使用

    一  使用环境: windows 7 64位操作系统 二  vue调试工具vue-devtools安装及使用 1.下载: 百度中查找  "vue-devtools下载"  找到最新 ...

  5. 基于url-to-pdf-api构建docker镜像,制作一个网页另存服务

    基于url-to-pdf-api构建docker镜像,制作一个网页另存服务 业务背景: 需要根据一个url路径打印这个网页的内容 解决方案: 1.使用wkhtml2pdf 2.使用puppeteer ...

  6. Spring IoC总结

    Spring 复习 1.Spring IoC 1.1 基本概念 1.1.1 DIP(Dependency Inversion Principle) 字面意思依赖反转原则,即调用某个类的构造器创建对象时 ...

  7. COM技术中的VARIANT and VARIANTARG

    VARIANT and VARIANTARG Use VARIANTARG to describe arguments passed within DISPPARAMS, and VARIANT to ...

  8. mysql主从复制,主主复制,级联复制,半同步复制

    -------------------------------------------------------------------------------主从复制----------------- ...

  9. 使用Tomcat30分钟搭建个人服务器

    目录 一.服务器简介 二.安装教程 三.出现的问题 一.服务器简介 Tomcat服务器是一个Apache软件资金会的开源项目,实现了Servlet,JSP,EL,WebSocket协议. 二.安装教程 ...

  10. 基于4H-SIC的先进集成电路用n型LDMOS晶体管

    基于4H-SIC的先进集成电路用n型LDMOS晶体管 摘要: 通过对具有不同的设计方式的具有减小的表面电场的横向4H-SIC-N型-横向扩散金属氧化物半导体(LDMOS)晶体管进行测量和模拟,得到了得 ...