In today’s interconnected world, location data plays a pivotal role in numerous applications, from e-commerce and logistics to travel and social networking. Efficiently managing and accessing this wealth of geographical information is crucial for developers. This article delves into the realm of location data management using Spring JPA (Java Persistence API), a powerful framework for object-relational mapping in Java applications.
We’ll embark on a journey to build a robust Spring service capable of handling intricate location data, encompassing countries, states, and cities. Our focus will be on structuring JPA entities, crafting repositories for seamless data access, and implementing a service layer to orchestrate the loading and retrieval of location information.
The data should be loaded using the following URL: https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/master/countries%2Bstates%2Bcities.json, or a similar URL adhering to the same data schema. This loading process should be incorporated within a change set in your application to avoid redundant data loading attempts.
1. Entities
- Country.java
import javax.persistence.*;
import java.util.List;
@Entity
public class Country {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String iso3;
private String iso2;
private String numericCode;
private String phoneCode;
private String capital;
private String currency;
private String currencyName;
private String currencySymbol;
private String tld;
private String nativeName;
private String region;
private String regionId;
private String subregion;
private String subregionId;
private String nationality;
@OneToMany(mappedBy = "country", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Timezone> timezones;
@Embedded
private Translations translations;
private String latitude;
private String longitude;
private String emoji;
private String emojiU;
@OneToMany(mappedBy = "country", cascade = CascadeType.ALL, orphanRemoval = true)
private List<State> states;
// Getters and setters for all attributes
}
- State.java
import javax.persistence.*;
import java.util.List;
@Entity
public class State {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String stateCode;
private String latitude;
private String longitude;
private String type;
@ManyToOne
@JoinColumn(name = "country_id")
private Country country;
@OneToMany(mappedBy = "state", cascade = CascadeType.ALL, orphanRemoval = true)
private List<City> cities;
// Getters and setters
}
- City.java
import javax.persistence.*;
@Entity
public class City {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String latitude;
private String longitude;
@ManyToOne
@JoinColumn(name = "state_id")
private State state;
// Getters and setters
}
- Timezone.java
import javax.persistence.*;
@Entity
public class Timezone {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String zoneName;
private int gmtOffset;
private String gmtOffsetName;
private String abbreviation;
private String tzName;
@ManyToOne
@JoinColumn(name = "country_id")
private Country country;
// Getters and setters
}
- Translations.java
import javax.persistence.Embeddable;
@Embeddable
public class Translations {
private String kr;
private String ptBR;
private String pt;
private String nl;
private String hr;
private String fa;
private String de;
private String es;
private String fr;
private String ja;
private String it;
private String cn;
private String tr;
private String ru;
private String uk;
private String pl;
// Getters and setters
}
2. Repositories
- CountryRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
List<Country> findByRegion(String region);
}
- StateRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface StateRepository extends JpaRepository<State, Long> {
List<State> findByCountryId(Long countryId);
}
- CityRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CityRepository extends JpaRepository<City, Long> {
List<City> findByStateId(Long stateId);
}
3. Service
- LocationService.java
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.util.List;
@Service
public class LocationService {
@Autowired
private CountryRepository countryRepository;
@Autowired
private StateRepository stateRepository;
@Autowired
private CityRepository cityRepository;
@Autowired
private RestTemplate restTemplate;
public void load(String url) {
try {
// 1. Fetch JSON from URL
String jsonData = restTemplate.getForObject(url, String.class);
// 2. Deserialize JSON into Java objects
ObjectMapper objectMapper = new ObjectMapper();
List<Country> countries = objectMapper.readValue(jsonData,
objectMapper.getTypeFactory().constructCollectionType(List.class, Country.class));
// 3. Save to the database
countryRepository.saveAll(countries);
} catch (IOException e) {
// Handle JSON parsing exceptions
e.printStackTrace();
}
}
public List<Country> getCountries() {
return countryRepository.findAll();
}
public List<State> getStatesByCountry(Long countryId) {
return stateRepository.findByCountryId(countryId);
}
public List<City> getCitiesByState(Long stateId) {
return cityRepository.findByStateId(stateId);
}
public List<Country> getCountriesByRegion(String region) {
return countryRepository.findByRegion(region);
}
}
Integration into a Spring Boot Project
-
Place these files:
- Place the entity classes (
Country.java
,State.java
,City.java
,Timezone.java
,Translations.java
) in your project’ssrc/main/java/.../model
directory. - Place the repository interfaces (
CountryRepository.java
,StateRepository.java
,CityRepository.java
) in your project’ssrc/main/java/.../repository
directory. - Place the service class (
LocationService.java
) in your project’ssrc/main/java/.../service
directory.
- Place the entity classes (
-
Configure Spring Data JPA:
- Ensure you have Spring Data JPA dependency in your
pom.xml
orbuild.gradle
. - Configure your database connection properties in your
application.properties
orapplication.yml
.
- Ensure you have Spring Data JPA dependency in your
-
Use the
LocationService
:- Autowire the
LocationService
into your controller or other components. - Call the
load
method to populate the database from the JSON URL. - Use the other methods (
getCountries
,getStatesByCountry
, etc.) to retrieve data as needed.
- Autowire the
Example Usage in a Controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class LocationController {
@Autowired
private LocationService locationService;
@GetMapping("/load-data")
public String loadData() {
locationService.load("https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/master/countries%2Bstates%2Bcities.json");
return "Data loading initiated!";
}
@GetMapping("/countries")
public List<Country> getAllCountries() {
return locationService.getCountries();
}
// ... other endpoints for states, cities, etc.
}
Enhancements
- PDF Parsing: Implement the
readJsonFromPdf
method in theLocationService
using a PDF parsing library to extract the JSON data from “location.pdf.” - Error Handling: Add comprehensive error handling and logging to the
load
method to gracefully manage potential exceptions. - Data Validation: Consider adding validation logic to the entities or service to ensure data integrity before saving to the database.
- API Documentation: Use tools like Swagger to document your
Discover more from GhostProgrammer - Jeff Miller
Subscribe to get the latest posts sent to your email.