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. Windows 常用命令与快捷键

    1.自选区截图Shift+win+s 2.全屏截图Prtsc 3.活动窗口截图Alt+Prtsc 4.新建文件夹Ctrl + Shift + N 5.返回上级目录Alt + 上方向键 6.后退到上一次 ...

  2. Project facet Java version 1.7 is not supported.解决方法

    最近遇到这个问题,在网上查到的解决方案基本都是下面几个: 1.右击项目,properties,project facets,改动java的version为1.7. 2.window,propertie ...

  3. Docker备份迁移

    目录 Docker备份迁移 1.容器保存为镜像 2.镜像打包成压缩文件 3.把压缩文件恢复成镜像 Docker备份迁移 1.容器保存为镜像 将已经装好各种软件的容器再次打包为镜像,这样下次直接装这个镜 ...

  4. Docker Hub 镜像加速器

    一.概述 国内从 Docker Hub 拉取镜像有时会遇到困难,此时可以配置镜像加速器.Docker 官方和国内很多云服务商都提供了国内加速器服务. 二.配置加速地址 Ubuntu 16.04+.De ...

  5. Kubernetes-2.组件

    内容主要摘自官网文档资料 官网地址 本文概述了交付正常运行的Kubernetes集群所需的各种组件. 本文编写基于kubernetes v1.17版本 目录 Kubernetes集群 Master组件 ...

  6. 12张图打开JMeter体系结构全局视角

    JMeter是Java技术栈工具,在软件测试领域应用非常广泛,无论是性能测试还是接口测试,技术都很成熟和稳定.它有一个突出特点:开源,适合做二次开发,以阿里为代表的Java技术栈公司都对它青睐有加.在 ...

  7. HDOJ-1160(最长上升子序列变形)

    FatMouse's Speed HDOJ-1160 注意输出长度的时候不是输出dp[n] #include<bits/stdc++.h> using namespace std; con ...

  8. c++:一个辅助类让内存泄漏现原形!

    前言 对于c++而言,如何查找内存泄漏是程序员亘古不变的话题:解决之道可谓花样繁多.因为最近要用到QT写程序,摆在我面前的第一个重要问题是内存防泄漏.如果能找到一个简单而行之有效的方法,对后续开发大有 ...

  9. mysql数据库的数据备份,以及开启日志

    导出数据: location代表需要保存的数据文件的位置,默认保存在 C:\ProgramData\MySQL\MySQL Server 5.7\Data(Windows10系统位置,其他系统位置自行 ...

  10. JAVA使用Collator对中文排序

    首先创建一个集合 public static List<String> init() { List<String> list = new ArrayList<String ...