Java Web Service with Retrofit CRUD & Image Upload Using Multipart
Author: Deep Singh | Category: Java, Android, Web Services
Overview
This tutorial demonstrates how to build a Spring + Hibernate based Java Web Service that supports full CRUD operations and image uploading via Multipart. On the client side, we use Retrofit in Android to consume the REST API, including multipart image upload.
By the end, you'll know how to:
- Set up a Spring MVC + Hibernate REST API
- Configure multipart file upload on the server
- Consume the API from Android using Retrofit
- Upload images using
MultipartBody.Part
Server-Side: Spring and Hibernate Based Web Service
Project Structure
AppConfig.java
package com.deepsingh44.config;
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.deepsingh44.model.Quotes;
@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan(basePackages = { "com.deepsingh44" })
@PropertySource(value = "classpath:/application.properties")
public class AppConfig {
@Autowired
private Environment environment;
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.class"));
dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
dataSource.setUsername(environment.getRequiredProperty("jdbc.user"));
dataSource.setPassword(environment.getRequiredProperty("jdbc.pass"));
return dataSource;
}
@Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setAnnotatedClasses(Quotes.class);
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
properties.put("hibernate.hbm2ddl.auto", environment.getRequiredProperty("hibernate.hbm2ddl.auto"));
return properties;
}
@Bean
public HibernateTransactionManager getTransactionManager() {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
@Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver cm = new CommonsMultipartResolver();
cm.setMaxUploadSize(10 * 1024 * 1024);
return cm;
}
}
FrontController.java
package com.deepsingh44.controller;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import com.deepsingh44.config.AppConfig;
public class FrontController extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { AppConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
QuotesController.java
package com.deepsingh44.controller;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.deepsingh44.model.Quotes;
import com.deepsingh44.service.QuotesService;
@RestController
public class QuotesController {
@Autowired
private QuotesService quotesService;
@PostMapping("/add")
public Quotes addQuotes(@RequestBody Quotes quotes) {
return quotesService.addQuotes(quotes);
}
@GetMapping("/quotes")
public List<Quotes> getAllQuotes() {
return quotesService.getAllQuotes();
}
@DeleteMapping("/quotes/{id}")
public Quotes deleteQuotes(@PathVariable int id) {
return quotesService.deleteQuotes(id);
}
@PutMapping("/update/{id}")
public Quotes updateQuotes(@PathVariable int id, @RequestBody Quotes quotes) {
return quotesService.updateQuotes(id, quotes);
}
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String uploadFile(@RequestParam("file") MultipartFile file, HttpServletRequest req) throws IOException {
String result = "Not Success";
String root = req.getServletContext().getRealPath("/");
File folder = new File(root, "images");
if (!folder.exists()) folder.mkdir();
File myfile = File.createTempFile("image", ".jpg", folder);
file.transferTo(myfile);
result = "Successfully Uploaded";
return result;
}
}
Quotes.java (Model)
package com.deepsingh44.model;
import javax.persistence.*;
@Entity
public class Quotes {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String title;
private String description;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
}
QuotesRepo.java
package com.deepsingh44.repo;
import java.io.Serializable;
import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.deepsingh44.model.Quotes;
@Repository
public class QuotesRepo {
@Autowired
private SessionFactory sessionFactory;
public Serializable addQuotes(Quotes quotes) {
return sessionFactory.getCurrentSession().save(quotes);
}
@SuppressWarnings({ "unchecked", "deprecation" })
public List<Quotes> getAllQuotes() {
return sessionFactory.getCurrentSession().createCriteria(Quotes.class).list();
}
public Quotes deleteQuotes(int id) {
Quotes quotes = getQuotesById(id);
sessionFactory.getCurrentSession().delete(quotes);
return quotes;
}
public Quotes getQuotesById(int id) {
return sessionFactory.getCurrentSession().get(Quotes.class, id);
}
public Quotes updateQuotes(int id, Quotes quotes) {
Quotes databasequotes = getQuotesById(id);
if (databasequotes != null) {
databasequotes.setTitle(quotes.getTitle());
databasequotes.setDescription(quotes.getDescription());
sessionFactory.getCurrentSession().update(databasequotes);
return databasequotes;
}
return new Quotes();
}
}
QuotesService.java
package com.deepsingh44.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.deepsingh44.model.Quotes;
import com.deepsingh44.repo.QuotesRepo;
@Service
public class QuotesService {
@Autowired
private QuotesRepo quotesRepo;
@Transactional
public Quotes addQuotes(Quotes quotes) {
return ((Integer) (quotesRepo.addQuotes(quotes)) > 0) ? quotes : new Quotes();
}
@Transactional
public List<Quotes> getAllQuotes() {
return quotesRepo.getAllQuotes();
}
@Transactional
public Quotes deleteQuotes(int id) {
return quotesRepo.deleteQuotes(id);
}
@Transactional
public Quotes updateQuotes(int id, Quotes quotes) {
return quotesRepo.updateQuotes(id, quotes);
}
}
application.properties
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.class=com.mysql.jdbc.Driver
jdbc.pass=root
jdbc.user=root
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.hbm2ddl.auto=update
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Hello-Spring-Mvc</groupId>
<artifactId>Hello-Spring-Mvc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>16</release>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.3</version>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.5.4.Final</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.3</version>
</dependency>
</dependencies>
</project>
Android Side: Retrofit Client
Project Structure
Quotes.java (Android Model)
package com.example.restapiconsume.model;
public class Quotes {
private int id;
private String image;
private String title;
private String description;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getImage() { return image; }
public void setImage(String image) { this.image = image; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
}
QuotesService.java (Retrofit Interface)
package com.example.restapiconsume.service;
import com.example.restapiconsume.model.Quotes;
import java.util.List;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.http.*;
public interface QuotesService {
@GET("quotes/")
Call<List<Quotes>> getQuotes();
@POST("add/")
Call<Quotes> addQuotes(@Body Quotes quotes);
@PUT("update/{id}")
Call<Quotes> updateQuotes(@Path("id") int id, @Body Quotes quotes);
@DELETE("quotes/{id}")
Call<Quotes> deleteQuotes(@Path("id") int id);
@Multipart
@POST("upload/")
Call<String> uploadFile(@Part MultipartBody.Part file, @Part("file") RequestBody name);
}
MainActivity.java
package com.example.restapiconsume;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.loader.content.CursorLoader;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import com.example.restapiconsume.model.Quotes;
import com.example.restapiconsume.service.QuotesService;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.File;
import java.io.IOException;
import java.util.List;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class MainActivity extends AppCompatActivity {
private EditText ttile, tdescription, tid;
private QuotesService quotesService;
private ImageView imageView, imagepicker;
private MultipartBody.Part body;
private RequestBody descBody;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SingleTask singleTask = (SingleTask) getApplication();
quotesService = singleTask.getRetrofit().create(QuotesService.class);
tid = findViewById(R.id.id);
ttile = findViewById(R.id.title);
tdescription = findViewById(R.id.description);
imageView = findViewById(R.id.mainimage);
imagepicker = findViewById(R.id.imagepicker);
imagepicker.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select image to upload"), 1001);
}
});
}
private String getRealPathFromURI(Uri contentUri) {
String[] proj = { MediaStore.Images.Media.DATA };
CursorLoader loader = new CursorLoader(this, contentUri, proj, null, null, null);
Cursor cursor = loader.loadInBackground();
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String result = cursor.getString(column_index);
cursor.close();
return result;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1001 && resultCode == RESULT_OK) {
Uri selectedImage = data.getData();
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), selectedImage);
imageView.setImageBitmap(bitmap);
File file = new File(getRealPathFromURI(selectedImage));
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
body = MultipartBody.Part.createFormData("file", file.getName(), requestFile);
descBody = RequestBody.create(MediaType.parse("application/json"), "this is image");
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void addQuotes(View view) {
Quotes quotes = new Quotes();
quotes.setId(Integer.parseInt(tid.getText().toString()));
quotes.setTitle(ttile.getText().toString());
quotes.setDescription(tdescription.getText().toString());
quotesService.addQuotes(quotes).enqueue(new Callback<Quotes>() {
@Override
public void onResponse(Call<Quotes> call, Response<Quotes> response) {
Log.e("error", response.body().toString());
}
@Override
public void onFailure(Call<Quotes> call, Throwable t) {
Log.e("error", t.toString());
}
});
}
public void deleteQuotes(View view) {
int id = Integer.parseInt(tid.getText().toString());
quotesService.deleteQuotes(id).enqueue(new Callback<Quotes>() {
@Override
public void onResponse(Call<Quotes> call, Response<Quotes> response) {
Log.e("error", response.body().toString());
}
@Override
public void onFailure(Call<Quotes> call, Throwable t) {
Log.e("error", t.toString());
}
});
}
public void updateQuotes(View view) {
int id = Integer.parseInt(tid.getText().toString());
Quotes quotes = new Quotes();
quotes.setTitle(ttile.getText().toString());
quotes.setDescription(tdescription.getText().toString());
quotesService.updateQuotes(id, quotes).enqueue(new Callback<Quotes>() {
@Override
public void onResponse(Call<Quotes> call, Response<Quotes> response) {
Log.e("error", response.body().toString());
}
@Override
public void onFailure(Call<Quotes> call, Throwable t) {
Log.e("error", t.toString());
}
});
}
public void allQuotes(View view) {
quotesService.getQuotes().enqueue(new Callback<List<Quotes>>() {
@Override
public void onResponse(Call<List<Quotes>> call, Response<List<Quotes>> response) {
Log.e("error", response.body().toString());
}
@Override
public void onFailure(Call<List<Quotes>> call, Throwable t) {
Log.e("error", t.toString());
}
});
}
public void uploadImage(View view) {
Gson gson = new GsonBuilder().setLenient().create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(SingleTask.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
QuotesService quotesService = retrofit.create(QuotesService.class);
quotesService.uploadFile(body, descBody).enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
Log.e("error", new Gson().toJson(response.body()));
}
@Override
public void onFailure(Call<String> call, Throwable t) {
Log.e("error", t.toString());
}
});
}
}
SingleTask.java
package com.example.restapiconsume;
import android.app.Application;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class SingleTask extends Application {
private static Retrofit retrofit;
public static final String BASE_URL = "http://192.168.1.26:9876/Hello-Spring-Mvc/";
@Override
public void onCreate() {
super.onCreate();
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
}
public Retrofit getRetrofit() {
return retrofit;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="wrap_content"
android:layout_gravity="center"
android:layout_height="wrap_content">
<ImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:id="@+id/mainimage"
android:scaleType="centerCrop"
android:src="@drawable/gallery"
android:contentDescription="Selected image preview" />
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="bottom|right"
android:src="@drawable/gallery"
android:id="@+id/imagepicker"
android:contentDescription="Pick image from gallery" />
</FrameLayout>
<EditText
android:id="@+id/id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Id"
android:padding="20dp"
android:inputType="number" />
<EditText
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Title"
android:padding="20dp"
android:inputType="text" />
<EditText
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Description"
android:padding="20dp"
android:inputType="text" />
<Button
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:onClick="addQuotes"
android:text="Add Quotes" />
<Button
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:onClick="allQuotes"
android:text="All Quotes" />
<Button
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:onClick="deleteQuotes"
android:text="Delete Quotes" />
<Button
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:onClick="updateQuotes"
android:text="Update Quotes" />
<Button
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:onClick="uploadImage"
android:text="Upload Image" />
</LinearLayout>
build.gradle (Module: app)
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.google.code.gson:gson:2.8.7'
Android Permissions
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Output Screen
No comments:
Post a Comment