1 Introduction & Overview

This article is based on the update of this article, mainly updating some versions of the stack and development tools, as well as fixing some bugs.

This article is a simple implementation of SpringBoot+Android+MySQL add, delete, check and change, using technology including Jackson, OkHttp, Bouncycastle, Spring Data JPA.

2 the environment

  • Android 4.1.2
  • The IDEA of 2020.3.1
  • Spring Boot 2.4.2
  • MySQL 8.0.23
  • OpenJDK 11

I’m going to skip the environment preparation, but if you need it, you can refer to it here.

3 the back-end

3.1 Creating a Project

Rely on:

3.2 Project Structure

3.3 entity class

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;
    private String password;
}
Copy the code

Basic Lombok annotations + two annotations in JPA:

  • @Id: Identifies the primary key
  • @GeneratedValue: primary key generation policy, including four

The primary key generation policy is as follows:

  • GenerationType.TABLE: Use a specific database table to store the primary key, independent of the external environment and the specific implementation of the database, but does not take full advantage of the database features, generally not preferred, and generally compatible@TableGeneratoruse
  • GenerationType.SEQUENCE: Some databases do not support primary key increment (e.gOracle), then you can use itSEQUENCE, only part (Oracle/DB2/PostgreSQL) supports sequence objects and is generally not used in other databases
  • GenerationType.IDENTITY: The primary key grows automatically. Data is automatically copied to the primary key when it is inserted, for exampleMySQLIn theauto_increment
  • GenerationType.AUTO: The primary key generation strategy is handed to the persistence engine, which will choose one of the three primary key strategies based on the databaseJPADefault primary key generation policy

3.4 the persistence layer

Inherit CrudRepository

, T is the entity class,ID is the primary key type:
,id>

@Repository
public interface UserRepository extends CrudRepository<User.Integer> {
    boolean existsByName(String name);
    User findByNameAndPassword(String name,String password);
}
Copy the code

One important point to note is that CrudRepository

inherits Repository

, which has a feature called query method, which can generate the corresponding SQL based on the specified keywords in some method, such as the first method existsByName, String name = String name = String name = String name = String name
,id>
,id>

3.5 the business layer

@Transactional
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserService {
    private final UserRepository repository;

    public boolean exists(User user){
        return repository.existsByName(user.getName());
    }

    public User findByNameAndPassword(User user){
        return repository.findByNameAndPassword(user.getName(),user.getPassword());
    }

    public boolean insert(User user){
        repository.save(user);
        return true;
    }

    public boolean update(User user){
        if(repository.findById(user.getId()).isEmpty()){
            return false;
        }
        repository.save(user);
        return true;
    }

    public boolean deleteById(int id){
        if(! repository.existsById(id)){return false;
        }
        repository.deleteById(id);
        return true; }}Copy the code

The notes are explained as follows:

  • @Transactional:
  • @Service: indicates the service layer. The actual effect is equivalent to@Component
  • @RequiredArgsConstructor:LombokA note to address the following warning:

Other methods that are known by their names are not explained.

3.6 control layer

@RestController
@RequestMapping("/")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserController {
    private final UserService service;

    @PostMapping("sign/in/up")
    public ResponseBody signInUp(@RequestBody User user) {
        if (service.exists(user)) {
            User u = service.findByNameAndPassword(user);
            return newResponseBody(u ! =null? ResponseCode.SIGN_IN_SUCCESS : ResponseCode.SIGN_IN_FAILED, u ! =null ? u.getId() : "");
        }
        return new ResponseBody(service.insert(user) ? ResponseCode.SIGN_UP_SUCCESS : ResponseCode.SIGN_UP_FAILED, "");
    }

    @PutMapping("update")
    public ResponseBody update(@RequestBody User user) {
        return new ResponseBody(service.update(user) ? ResponseCode.UPDATE_SUCCESS : ResponseCode.UPDATE_FAILED, "");
    }

    @DeleteMapping("delete")
    public ResponseBody deleteByName(@RequestParam int id) {
        return new ResponseBody(service.deleteById(id) ? ResponseCode.DELETE_SUCCESS : ResponseCode.DELETE_FAILED, "");
    }

    @GetMapping("test")
    public String test(a) {
        return "test"; }}Copy the code

The notes are explained as follows:

  • @RestController: equivalent to the@ResponseBody+@Controller.@RepsonseBodyIs an annotation that returns data directly (not the default view name), and@Controllerwith@ServiceSimilarly, view the source code can know are@ComponentThe alias
  • @RequestMapping: represents those contained in methods in this classMappingAll start with this value
  • @PostMapping/@PutMapping/@DeleteMapping/@GetMapping: Identification processingPOST/PUT/Delete/GETRequested path, if added on the class@RequestMapping, the path is spliced in@RequestMappingThe back of, like here@GetMapping("test")The equivalent of/test

3.7 Response body + Response Code

Response body:

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class ResponseBody {
    private int code;
    private Object data;
}
Copy the code

The response code:

public class ResponseCode {
    public static final int SIGN_IN_SUCCESS = 2000;
    public static final int SIGN_UP_SUCCESS = 2001;
    public static final int UPDATE_SUCCESS = 2002;
    public static final int DELETE_SUCCESS = 2003;

    public static final int SIGN_IN_FAILED = 3000;
    public static final int SIGN_UP_FAILED = 3001;
    public static final int UPDATE_FAILED = 3002;
    public static final int DELETE_FAILED = 3003;
}
Copy the code

3.8 Configuration File

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/userinfo

  jpa:
    open-in-view: false
    hibernate:
      ddl-auto: update
Copy the code

The default value of open-in-view is true in JPA. The value of open-in-view is set to false to suppress a warning that lazy-loaded data can be accessed outside of transactions, which may cause manual data source switching failure. So set it to false.

Ddl-auto: update indicates that the data table is updated. The original data is retained and the table can be created automatically when no table is created. There are five options for setting this parameter: update, create, create-drop, validate, and None. You can see the differences here.

3.9 test

Localhost :8080/test

That’s it. The rest needs to be tested on the Android side.

4 Androidend

4.1 Creating a Project

Android Q + Java.

4.2 Dependency + Rights

Rely on:

implementation 'com. Squareup. Okhttp3: okhttp: 4.9.0'
implementation 'com. Fasterxml. Jackson. Core: Jackson - databind: 2.12.1'
implementation 'org. Bouncycastle: bcprov - jdk15to18:1.68'
implementation "Org. Projectlombok: lombok: 1.18.16."
annotationProcessor 'org. Projectlombok: lombok: 1.18.16'
Copy the code

Jurisdiction:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<! - in - < application > >
android:usesCleartextTraffic="true"
Copy the code

Open viewBinding:

buildFeatures{
    viewBinding = true
}
Copy the code

4.3 Project Structure

4.4 entity class

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class User {
    private int id;
    private String name;
    private String password;

    public User(String name, String password) {
        this.name = name;
        this.password = password; }}Copy the code

4.5 tools

public class Utils {
    private static final Keccak.Digest512 digest512 = new Keccak.Digest512();

    public static String encrypt(String origin) {
        return new String(Hex.encode(digest512.digest(origin.getBytes(StandardCharsets.UTF_8))));
    }

    public static String getResponseMessage(int code) {
        String message = "";
        switch (code) {
            case ResponseCode.SIGN_IN_SUCCESS:
                message = "Login successful";
                break;
            case ResponseCode.SIGN_UP_SUCCESS:
                message = "Registration successful";
                break;
            case ResponseCode.SIGN_IN_FAILED:
                message = "Wrong username or password";
                break;
            case ResponseCode.SIGN_UP_FAILED:
                message = "Registration failed";
                break;
            case ResponseCode.DELETE_FAILED:
                message = "Delete failed";
                break;
            case ResponseCode.DELETE_SUCCESS:
                message = "Delete successful, exit automatically";
                break;
            case ResponseCode.UPDATE_SUCCESS:
                message = "Update successful";
                break;
            case ResponseCode.UPDATE_FAILED:
                message = "Update failed";
                break;
            case ResponseCode.EMPTY_RESPONSE:
                message = "Response body is empty";
                break;
            case ResponseCode.SERVER_ERROR:
                message = "Server error";
                break;
            case ResponseCode.JSON_SERIALIZATION:
                message = "JSON serialization error";
                break;
            case ResponseCode.EXIT_SUCCESS:
                message = "Exit successful";
                break;
            case ResponseCode.REQUEST_FAILED:
                message = "Request sending failed";
                break;
            case ResponseCode.UNCHANGED_INFORMATION:
                message = "Unmodified message";
                break;
        }
        return message;
    }

    public static void showMessage(Context context, Message message) { Toast.makeText(context, getResponseMessage(message.what), Toast.LENGTH_SHORT).show(); }}Copy the code

The utility class has three methods:

  • Encryption: The passwordSHA3-512The encrypted password is sent to the backend
  • Obtain corresponding information: according toMessageObtain the corresponding prompt information
  • Display information: useToastDisplay information

4.6 Response body + Response Code

Response body:

@NoArgsConstructor
@Setter
@Getter
public class RestResponse {
    private int code;
    private Object data;
}
Copy the code

The response code:

public class ResponseCode {
    public static final int SIGN_IN_SUCCESS = 2000;
    public static final int SIGN_UP_SUCCESS = 2001;
    public static final int UPDATE_SUCCESS = 2002;
    public static final int DELETE_SUCCESS = 2003;

    public static final int SIGN_IN_FAILED = 3000;
    public static final int SIGN_UP_FAILED = 3001;
    public static final int UPDATE_FAILED = 3002;
    public static final int DELETE_FAILED = 3003;

    public static final int EMPTY_RESPONSE = 4000;
    public static final int SERVER_ERROR = 4001;
    public static final int REQUEST_FAILED = 4002;
    public static final int JSON_SERIALIZATION = 4003;
    public static final int EXIT_SUCCESS = 4004;
    public static final int UNCHANGED_INFORMATION = 4005;
}
Copy the code

4.7 the requestURLconstant

public class NetworkSettings {
    private static final String HOST = "192.168.1.8";
    private static final String PORT = "8080";
    public static final String SIGN_IN_UP = "http://"+ HOST +":"+PORT + "/sign/in/up";
    public static final String UPDATE = "http://"+ HOST +":"+PORT + "/update";
    public static final String DELETE = "http://"+ HOST +":"+PORT + "/delete";
}
Copy the code

4.8 MainActivity

The last part of the code, most of the rest is similar, just look at the source link.

public void signInUp(View view) {
    try {
        String name = binding.name.getText().toString();
        / / encryption SHA3-512
        String password = Utils.encrypt(binding.password.getText().toString());
        // Construct the OkHttp Request Request
        Request request = new Request.Builder().url(NetworkSettings.SIGN_IN_UP).post(
        	// The request body type is application/json; Charset = UTF-8, using Jackson serialized to JSON
            RequestBody.create(mapper.writeValueAsString(new User(name, password)), mediaType)
        ).build();
        // An asynchronous POST operation, passing in a Callback
        client.newCall(request).enqueue(new Callback() {
        	/ / if the failure
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
            	// Request failed
                message.what = ResponseCode.REQUEST_FAILED;
                Make (getApplicationContext(),"message", toast.length_short).show()
                // Since it is not the same thread, we need to use Handler to submit, namely the post() method, which takes a thread as argument
                handler.post(()->Utils.showMessage(getApplicationContext(),message));
                e.printStackTrace();
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
            	// If successful
                if (response.isSuccessful()) {
                	// Get the request body
                    ResponseBody body = response.body();
                    // If the response body is not empty
                    if(body ! =null) {
                    	// Deserialize to the response body, which contains a response code and data fields
                        RestResponse restResponse = mapper.readValue(body.string(), RestResponse.class);
                        / / set the Message
                        message.what = restResponse.getCode();
                        // If the login succeeds
                        if(message.what == ResponseCode.SIGN_IN_SUCCESS){
                            handler.post(()->{
                            	// Storage user ID
                                signInId = (int)restResponse.getData();
                                / / update the UI
                                binding.update.setVisibility(View.VISIBLE);
                                binding.delete.setVisibility(View.VISIBLE);
                                binding.signInUp.setText("Quit");
                                binding.signInUp.setOnClickListener(v->signOut(false));
                                // Save the old username and password for updatingoldName = binding.name.getText().toString(); oldPassword = binding.password.getText().toString(); }); }}else {
                    	// Empty response body
                        message.what = ResponseCode.EMPTY_RESPONSE;
                        Log.e("RESPONSE_BODY_EMPTY", response.message()); }}else {
                	// Server error
                    message.what = ResponseCode.SERVER_ERROR;
                    Log.e("SERVER_ERROR", response.message());
                }
                // Follow the Message prompthandler.post(()->Utils.showMessage(getApplicationContext(),message)); }}); }catch(JsonProcessingException e) { message.what = ResponseCode.JSON_SERIALIZATION; Utils.showMessage(getApplicationContext(),message); e.printStackTrace(); }}Copy the code

This section is the code for login and registration, as well as the code for updating the user information and deleting the user, mostly similar.

5 test

6 Precautions

If there is a problem and some functions cannot be implemented properly, please refer to the DOS and don ‘ts and solutions here.

7 source

Two implementations of Java+Kotlin are provided:

  • Github
  • Yards cloud
  • CODE.CHINA

8 Reference Links

1. @GeneratedValue annotation of CSDN-JPA

Spring Boot JPA learning: 2.DAO and Service self-add ID, delete, query, change operations

If you think the article looks good, please like it.

At the same time, welcome to pay attention to wechat public number: Lingzhi Road.