1 Introduction & Overview
This article is based on an update to this article, updating some of the technology stacks to be more relevant to actual needs, and fixing some bugs.
This is a front-end Android+ back-end Java/Kotlin through Servelt background database (MySQL) interaction detailed steps and source code implementation, technical stack:
Android
basis- native
JDBC
+ nativeServlet
Tomcat
+MySQL
(Docker
)
Of course, a lot of Java backend development today uses Spring Boot instead of native servlets, so using Spring Boot for implementation is another article for me.
Although the Spring Boot implementation is very simple, the underlying principles are better understood using native servlets. In addition, this article is a basic tutorial, many steps will be more detailed and attached with the diagram, good nonsense do not say, the body begins.
2 the environment
Android Studio 4.1.2
IntelliJ IDEA 2020.3
MySQL 8.0.23
Tomcat 10.0
Docker 20.10.1
- The server
CentOS 8.1.1911
3 Environment Preparation
3.1 IDE
To prepare
Install Android Studio+IDEA on the official website, which is omitted.
3.2 MySQL
3.2.1 Installation Overview
MySQL refers to MySQL Community unless otherwise specified.
MySQL provides exe installation package on Windows:
DMG installation packages are available under macOS:
You can download it here.
In Linux, MySQL can be installed in the following ways:
- Software package installation (
apt/apt-get
,yum
,dnf
,pacman
Etc.) - Download the package and install it
- Source code compilation and installation
Docker
The installation
Among them, the relatively easy installation method is Docker installation and software package installation, followed by the compressed package installation, especially do not recommend the source installation (of course, if you like the challenge can refer to the author of a compilation installation 8.0.19 and compilation installation 8.0.20).
3.2.2 The installation starts
Here, the author chose to use Docker installation in the local test, and the steps can be seen here.
In addition, for the server, you can also use Docker installation, if the use of software package installation, here to the author’s CentOS8 as an example, other systems for reference as follows:
- Fedroa
- RedHat
- Ubuntu
3.2.2.1 Download and Install the software
Add warehouse:
sudo yum install https://repo.mysql.com/mysql80-community-release-el8-1.noarch.rpm
Copy the code
Disable the default MySQL module (CentOS8 includes a default MySQL module, so you cannot use the repository installed above) :
sudo yum module disable mysql
Copy the code
Installation:
sudo yum install mysql-community-server
Copy the code
3.2.2.2 Start the Service and View the Initial Password
Start the service:
systemctl start mysqld
Copy the code
To view temporary passwords:
sudo grep 'temporary password' /var/log/mysqld.log
Copy the code
Enter a temporary password to log in:
mysql -u root -p
Copy the code
Change password:
alter user 'root'@'localhost' identified by 'PASSWORD'
Copy the code
3.2.2.3 Creating External Access Users
You are not advised to directly access the root user in Java. Instead, you need to create a user with the corresponding permission to access the root user, which is omitted for convenience.
3.3 Tomcat
3.3.1 localTomcat
Tomcat is easy to install and can be downloaded directly from the official website:
Extract:
Tar ZXVF - apache tomcat - 10.0.0, tar, gzCopy the code
Go to the bin directory and run startup.sh:
cdApache tomcat - 10.0.0 / bin/startup. ShCopy the code
Local access localhost:8080:
That counts as a success. For Windows users, you can download the file and run startup.bat to access localhost:8080.
3.3.2 rainfall distribution on 10-12 serverTomcat
The server can be installed directly using wget:
Wget HTTP: / / https://downloads.apache.org/tomcat/tomcat-10/v10.0.0/bin/apache-tomcat-10.0.0.tar.gzCopy the code
However, this speed is very slow, it is recommended to download to local and then use SCP upload:
SCP apache tomcat - 10.0.0. Tar. Gz [email protected]: /Copy the code
Run startup.sh to access public IP address :8080 and check whether the file is decompressed.
4. Build database and build table
4.1 the users table
The MySQL script used here is as follows:
CREATE DATABASE userinfo;
USE userinfo;
CREATE TABLE user
(
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name CHAR(30) NULL,
password CHAR(30) NULL
)
Copy the code
4.2 the import
mysql -u root -p < user.sql
Copy the code
5 Back-end
Since this is a very basic tutorial, let’s start by creating the project.
5.1 Creating a Project + Guide Library
Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise: Java Enterprise:
2020.3 IDEA adds the function of selecting libraries more humanized than before. By default, servlets are selected. If you need other libraries, you can choose them by yourself.
Another thing to note is that JavaEE has been renamed JakartaEE, so you can select JakartaEE here:
Fill in the corresponding package name and select the location:
After creating the Servlet package, I encountered an error and could not find the corresponding Servlet package:
Select update Central Repository in Settings:
The created directory looks like this:
We then add dependencies, including:
MySQL
Jackson
Lombok
Add to pom.xml (note the version, you can check here for different MySQL versions) :
<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>
Copy the code
That completes the first step.
5.2 structure
The project structure is as follows:
- Persistence layer operations:
Dao
- Entity class:
User
- Response body:
ResponseBody
Servlet
Layer:SignIn
/SignUp
/Test
- Tools:
DBUtils
Start the class: No, because inWeb
Running on the server
Create files and directories:
5.3 DBUtils
Native JDBC fetch connection utility class:
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(a) {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
final String url = "JDBC: mysql: / / 127.0.0.1:3306 / the 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(a) {
if(connection ! =null) {
try {
connection.close();
} catch(SQLException e) { e.printStackTrace(); }}}}Copy the code
Focus on these four lines:
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "JDBC: mysql: / / 127.0.0.1:3306 / the userinfo";
String username = "root";
String password = "123456";
Copy the code
Note the difference between MySQL8’s registration driver and the older version:
Class.forName("com.mysql.jdbc.Driver");
Copy the code
5.4 User
Three fields + @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; }}Copy the code
5.5 Dao
Database operation layer:
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();
returnpreparedStatement.getUpdateCount() ! =0;
} catch (SQLException e) {
e.printStackTrace();
return false;
} finally{ DBUtils.closeConnection(); }}}Copy the code
Two operations:
- Query: The system returns if the user exists
true
, otherwise,false
- Insert: Adds a user
Note the use in insert operationsexecuteUpdate()
Insert and use at the same timegetUpdateCount() ! = 0
Judge the result of insertion rather than use it directly
return preparedStatement.execute();
Copy the code
In general:
select
:executeQuery()
.executeQuery()
returnResultSet
Represents the result set, savedselect
Statement execution result, matchnext()
usedelete
/insert
/update
Use:executeUpdate()
.executeUpdate()
Returns an integer representing the number of rows affected, i.edelete
/insert
/update
The number of lines modified, fordrop
/create
Operation returns0
create
/drop
Use:execute()
.execute()
Is returned if the first result isResultSet
Object is returnedtrue
If the first result is an update count or there is no resultfalse
So in this case
return preparedStatement.execute();
Copy the code
Must return false, cannot directly determine whether the insert was successful.
5.6 response body
Add a response body class to set the return code and data:
package com.example.javawebdemo.response;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class ResponseBody{
private Object data;
private int code;
}
Copy the code
5.7 Servlet
SingIn
Class to handle logins, callsJDBC
Check whether the database has a corresponding userSignUp
Class is used to process the registration of theUser
Add to the databaseTest
To test theServlet
, returns a fixed string
On the first 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); }}Copy the code
Note:
@WebServlet
Definition:Servlet
(It is possible to leave this comment out but it needs to be inweb.xml
Manual definition inServlet
), the default property isvalue
Said,Servlet
The path- Code:
HttpServletRequest
/HttpServletResponse
All set upUTF8
(although not necessary in this case because there are no Chinese characters) - Get parameters:
request.getParameter
Gets the parameters from the request. The parameters passed in are key values - Write response body: use
Jackson
That will beresponse.getWriter
And the response body is passed in, and then handed inmapper.writeValue
Write the response body
Here is signup.java, much of the code is similar:
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); }}Copy the code
Test the 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"); }}Copy the code
5.8 run
To run Tomcat, select Tomcat Server in the run configuration:
Set the Tomcat root directory:
Then, after Deployment + is selected, select the second one that is equipped with exploded (although the first one is not impossible, although the first one is usually published to the remote version in the FORM of a WAR, while the second one directly copies all the files into the webapps directory in the current format and supports hot Deployment in debug mode) :
In addition, you can change the path to a relatively simple path, convenient operation:
Debug (run without hot deployment) :
Localhost :8080/demo (IDEA should be automatically opened)
Test will appear under the access path:
Now that the back-end is done, let’s deal with the Android side.
6 Android
end
6.1 Creating a Project
6.2 Dependencies/Permissions
Dependencies are as follows:
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.12.1'
Copy the code
Add build. Gradle to build.
buildFeatures{
viewBinding = true
}
Copy the code
ViewBinding is the view binding function. Previously, findViewById was used to obtain the corresponding component, and then there was Butter Knife. Now Butter Knife is out of date, and View Binding is recommended.
Add network permissions to androidmanifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Copy the code
You also need to add HTTP support, because this is a sample Demo will not use HTTPS, but the current version of Android does not support it by default, so you need to add in
:
android:usesCleartextTraffic="true"
Copy the code
6.3 Project Structure
Four files:
MainActivity
Core:Activity
NetworkSettings
Request:URL
, constants,NetworkThread
: Network request threadResponseBody
: request body
6.4 ResponseBody
package com.example.androiddemo;
public class ResponseBody {
private int code;
private Object data;
public int getCode(a) {
return code;
}
public Object getData(a) {
returndata; }}Copy the code
Response body, one return code field + one data field.
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";
}
Copy the code
Set HOST to your own Intranet IP address. Do not use localhost/127.0.0.1.
You can use IP addr, ifconfig, and ipconfig to view your Intranet IP address:
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(a){
try {
// Open the connection
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
// Splice data
String data = "name="+ URLEncoder.encode(name, StandardCharsets.UTF_8.toString())+"&password="+URLEncoder.encode(password,StandardCharsets.UTF_8.toString());
// Set the request method
connection.setRequestMethod("POST");
// Allow input/output
connection.setDoInput(true);
connection.setDoOutput(true);
// Write data (i.e. send data)
connection.getOutputStream().write(data.getBytes(StandardCharsets.UTF_8));
byte [] bytes = new byte[1024];
// Get the returned data
int len = connection.getInputStream().read(bytes);
return new String(bytes,0,len,StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
return ""; }}}Copy the code
The thread class that sends the network request implements the Callable
interface because it is an asynchronous thread, which means that it returns String data. The main thread can get the return value by blocking with 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 gets the thread return value, deserialized to ResponseBody with ObjectMapper
ResponseBody body = mapper.readValue(signInTask.get(),ResponseBody.class);
// Determine the prompt according to the return code
Toast.makeText(getApplicationContext(),body.getCode() == 200 ? "Login successful" : "Login failed",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 ? "Registration successful" : "Registration failed",Toast.LENGTH_SHORT).show();
}catch(Exception e){ e.printStackTrace(); }}}Copy the code
So viewBinding, in onCreate:
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
Copy the code
Use the ActivityMainBinding static method to obtain a binding. Note that the ActivityMainBinding class name is not fixed.
6.8 Resource Files
Two:
activity_main.xml
strings.xml
The differences are as follows without going into details:
<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>
Copy the code
<resources>
<string name="app_name">AndroidDemo</string>
<string name="name">The user name</string>
<string name="password">password</string>
<string name="signUp">registered</string>
<string name="signIn">The login</string>
</resources>
Copy the code
7 test
7.1 Local Tests
First run the Java Web side, which should automatically open the following interface:
After adding test:
Run the Android terminal, enter a non-existent user name or password first, prompting login failure, then register, and login success:
Check the back-end database as follows:
7.2 Deployment Test
Ensure that the user name and password of the local database are the same as those of the server. There are corresponding tables and libraries
Add a
to pom.xml before deploying the Java Web side:
On the toolbar on the right, select Clean, then Compile, and finally Package:
The reason for doing this is that if a file is updated, packaging does not package the updated file back into it, so the original bytecode file needs to be cleaned up first, and then compiled and packaged.
A demo.war will appear under target after completion:
SCP (or some other tool) uploads to the server and moves to Tomcat’s Webapps (assuming the server’s IP is 8.8.8.8 for the sake of illustration) :
SCP demo. War 8.8.8.8 / XXX# After connecting to the server over SSH
cp demo.war /usr/local/tomcat/webapps
Copy the code
Start Tomcat:
cd /usr/local/tomcat/bin
./startup.sh
Copy the code
After startup, you can see a new demo folder in webapps:
Visit 8.8.8.8/ Demo to see the local test page. Android NetworkSettings HOST: 8.8.8.8
Server database:
8 Precautions
Notes are trivial and a little too many, so I started a separate blog post, here.
If you have any other questions, please leave a comment.
Nine source
Java+Kotlin is provided in two languages:
- Github
- Yards cloud
- CODE.CHINA
If you think the article looks good, please like it.
At the same time, welcome to pay attention to wechat public number: Lingzhi Road.