Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
401a9b9
feat: add domain classes
Sep 25, 2024
2776890
feat: configuration for database
Sep 25, 2024
9235dba
feat: configuration for redis
Sep 25, 2024
35a31ea
feat: method of getting all data from mysql
Sep 25, 2024
875e3cb
feat: add country services
Sep 25, 2024
04e70ae
feat: add redis services
Sep 25, 2024
f826fc8
feat: add city services
Sep 26, 2024
e032662
feat: add classes with fields that often called in request
Sep 26, 2024
0d8f3df
refactor: move non business logic to CityDataProcessing class
Sep 26, 2024
2d8f1fe
refactor: move non business logic to CityDataProcessing class
Sep 26, 2024
b776079
refactor: move non business logic to CityDataProcessing class
Sep 26, 2024
647da17
style: comments delete
Sep 26, 2024
98a30a4
style: comments delete
Sep 26, 2024
e7acccf
style: comments delete
Sep 26, 2024
44a0692
feat: add city-country transformer
Sep 26, 2024
17ab566
feat: build feat
Sep 26, 2024
b948e6f
test: tests add
Sep 26, 2024
e0bf1ea
style: remove comments
Sep 26, 2024
1c354a0
refactor: names refactor
Sep 26, 2024
721adee
fix: add other lombok annotation instead of @Data which don't cause e…
Oct 5, 2024
933d766
refactor: add better structure for domain classes
Oct 5, 2024
de7cd6c
feat: add CrudRepository
Oct 5, 2024
809dadf
feat: add config class
Oct 5, 2024
37ce77f
feat: add repository classes
Oct 5, 2024
c59626f
feat: add more database queries
Oct 5, 2024
9c0db65
remove: remove unnecessary classes
Oct 5, 2024
bdb103a
feat: add customize exceptions
Oct 5, 2024
8276d0f
refactor: name changed
Oct 5, 2024
ed752cb
feat: add services
Oct 5, 2024
dc7907a
refactor: add methods
Oct 5, 2024
61e06e0
refactor: change variable type
Oct 5, 2024
928b932
fix: bug fixes
Oct 5, 2024
06b06ec
feat: add redis exception
Oct 5, 2024
1fccc38
fix: bug fixes
Oct 5, 2024
498cd0c
feat: add redis logic for the most requested items
Oct 6, 2024
733093f
feat: add config class
Oct 6, 2024
d32696d
fix: bugs fixed
Oct 6, 2024
820bc97
fix: bugs fixed
Oct 6, 2024
0bb5776
feat: add class for data transforming
Oct 6, 2024
f49de32
style: delete unused imports
Oct 6, 2024
db4c0cb
fix: bugs fixed
Oct 6, 2024
2d87e84
style: remove unnecessary code
Oct 6, 2024
5eddd8d
fix: bugs fixes
Oct 6, 2024
3422b10
test: add test class
Oct 6, 2024
30f8b6a
refactor: remove catches
Oct 8, 2024
f0bd420
refactor: edit logs
Oct 8, 2024
18f463f
refactor: edit pom.xml
Oct 8, 2024
ce4838b
refactor: edit main
Oct 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>my-project</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>


<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>

<dependency>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

first dependency, then plugins

<groupId>org.hibernate</groupId>
<artifactId>hibernate-core-jakarta</artifactId>
<version>5.6.14.Final</version>
</dependency>

<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>

<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.2.2.RELEASE</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.12.0</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.12.0</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.13</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.0</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.2.0</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
124 changes: 124 additions & 0 deletions src/main/java/com/javarush/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package com.javarush;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.javarush.cache.RedisRepository;
import com.javarush.config.HibernateUtil;
import com.javarush.config.RedisConfig;
import com.javarush.domain.entity.City;
import com.javarush.domain.entity.CountryLanguage;
import com.javarush.redis.CityCountry;
import com.javarush.repository.CityRepository;
import com.javarush.repository.CountryRepository;
import com.javarush.services.CityService;
import com.javarush.services.CountryService;
import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisStringCommands;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static java.util.Objects.nonNull;

public class Application {
public final SessionFactory sessionFactory;
public final RedisClient redisClient;
public final CityService cityService;
public final CountryService countryService;
public final ObjectMapper mapper;
private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);

public Application() {
sessionFactory = HibernateUtil.getSessionFactory();
redisClient = RedisConfig.prepareRedisClient();
cityService = new CityService(new RedisRepository(), new CityRepository());
countryService = new CountryService(new RedisRepository(), new CountryRepository());
mapper = new ObjectMapper();
}

private void shutdown() {
if (nonNull(sessionFactory)) {
sessionFactory.close();
}
if (nonNull(redisClient)) {
redisClient.shutdown();
}
}

public static void main(String[] args) {
int numOfQueries = 15;
Application application = new Application();
RedisRepository countryRedisRepository = new RedisRepository();
RedisRepository cityRedisRepository = new RedisRepository();

System.out.println("Querying Country by ID...");
for (int i = 0; i < numOfQueries; i++) {
application.countryService.getById(1);
application.cityService.getById(2);
}
}

private void pushToRedis(List<CityCountry> data) {
try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
RedisStringCommands<String, String> sync = connection.sync();
for (CityCountry cityCountry : data) {
try {
sync.set(String.valueOf(cityCountry.getId()), mapper.writeValueAsString(cityCountry));
} catch (JsonProcessingException e) {
LOGGER.error("Couldn't push to redis : ", e);
e.printStackTrace(System.out);
}
}

}
}

private void testRedisData(List<Integer> ids) {
try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
RedisStringCommands<String, String> sync = connection.sync();
for (Integer id : ids) {
String value = sync.get(String.valueOf(id));
try {
mapper.readValue(value, CityCountry.class);
} catch (JsonProcessingException e) {
LOGGER.error("Couldn't test redis data : ", e);
e.printStackTrace(System.out);
}
}
}
}

public void testMysqlData(List<Integer> ids) {
try (Session session = sessionFactory.getCurrentSession()) {
session.beginTransaction();
for (Integer id : ids) {
City city = cityService.getById(id);
Set<CountryLanguage> languages = city.getCountryId().getLanguages();
}
session.getTransaction().commit();
}
}

public List<City> fetchData() {
try (Session session = sessionFactory.getCurrentSession()) {
List<City> allCities = new ArrayList<>();
session.beginTransaction();

countryService.getAll();

int totalCount = cityService.getTotalCount();
int step = 500;
for (int i = 0; i < totalCount; i += step) {
allCities.addAll(cityService.getItems(i, step));
}
session.getTransaction().commit();
return allCities;
}
}
}
72 changes: 72 additions & 0 deletions src/main/java/com/javarush/DataTransformer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.javarush;

import com.javarush.domain.entity.City;
import com.javarush.domain.entity.Country;
import com.javarush.domain.entity.CountryLanguage;
import com.javarush.redis.CityCountry;
import com.javarush.redis.Language;

import java.util.Set;
import java.util.stream.Collectors;

public class DataTransformer {
public static CityCountry countryTransformToCityCountry(Country country) {
CityCountry res = new CityCountry();
res.setAlternativeCountryCode(country.getCode2());
res.setContinent(country.getContinent());
res.setCountryCode(country.getCode());
res.setCountryName(country.getName());
res.setCountryPopulation(country.getPopulation());
res.setCountryRegion(country.getRegion());
res.setCountrySurfaceArea(country.getSurfaceArea());
Set<CountryLanguage> countryLanguages = country.getLanguages();
Set<Language> languages = countryLanguages.stream().map(cl -> {
Language language = new Language();
language.setLanguage(cl.getLanguage());
language.setIsOfficial(cl.getIsOfficial());
language.setPercentage(cl.getPercentage());
return language;
}).collect(Collectors.toSet());
res.setLanguages(languages);
return res;
}

public static CityCountry cityTransformToCityCountry(City city) {
CityCountry res = new CityCountry();
res.setId(city.getId());
res.setName(city.getName());
res.setPopulation(city.getPopulation());
res.setDistrict(city.getDistrict());
return res;
}

public static City cityCountryTransformToCity(CityCountry cityCountry) {
City city = new City();
city.setId(cityCountry.getId());
city.setName(cityCountry.getName());
city.setPopulation(cityCountry.getPopulation());
city.setDistrict(cityCountry.getDistrict());
return city;
}

public static Country cityCountryToCountry(CityCountry cityCountry) {
Country country = new Country();
country.setCode2(cityCountry.getAlternativeCountryCode());
country.setContinent(cityCountry.getContinent());
country.setCode(cityCountry.getCountryCode());
country.setName(cityCountry.getCountryName());
country.setPopulation(cityCountry.getCountryPopulation());
country.setRegion(cityCountry.getCountryRegion());
country.setSurfaceArea(cityCountry.getCountrySurfaceArea());
Set<Language> languages = cityCountry.getLanguages();
Set<CountryLanguage> countryLanguages = languages.stream().map(cl -> {
CountryLanguage countryLanguage = new CountryLanguage();
countryLanguage.setLanguage(cl.getLanguage());
countryLanguage.setIsOfficial(cl.getIsOfficial());
countryLanguage.setPercentage(cl.getPercentage());
return countryLanguage;
}).collect(Collectors.toSet());
country.setLanguages(countryLanguages);
return country;
}
}
58 changes: 58 additions & 0 deletions src/main/java/com/javarush/cache/RedisRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.javarush.cache;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.javarush.services.CityService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.util.SafeEncoder;

public class RedisRepository {
private final UnifiedJedis jedis;
private final ObjectMapper objectMapper = new ObjectMapper();
private static final String TOPK_NAME = "city-country";
private static final int THRESHOLD = 7;
private static final Logger LOGGER = LoggerFactory.getLogger(CityService.class);
private static final String REDIS_URL = "redis://127.0.0.1:6379";
private static final String BRACES_REGEX = "[\\[\\]]";

public RedisRepository() {
jedis = new UnifiedJedis(REDIS_URL);

jedis.del(TOPK_NAME);
if (!jedis.exists(TOPK_NAME)) {
jedis.topkReserve(TOPK_NAME, 5L, 2000L, 7L, 0.925D);
}
}

public <T> T getById(String key, Class<T> clazz) throws JsonProcessingException {
String serializedEntity = jedis.get(key);
return deserialize(serializedEntity, clazz);
}

public <T> void put(String key, T value) throws JsonProcessingException {
jedis.topkAdd(TOPK_NAME, key);

if (getCount(key) >= THRESHOLD) {
jedis.set(key, serialize(value));
}
}

private int getCount(String key) {
String count = jedis.sendCommand(() -> SafeEncoder.encode("TOPK.COUNT"), SafeEncoder.encodeMany(TOPK_NAME, key)).toString();
return Integer.parseInt(count.replaceAll(BRACES_REGEX, ""));
}

public boolean checkExists(String key) {
return jedis.exists(key);
}

private <T> String serialize(T entity) throws JsonProcessingException {
return objectMapper.writeValueAsString(entity);
}

private <T> T deserialize(String serializedEntity, Class<T> clazz) throws JsonProcessingException {
return objectMapper.readValue(serializedEntity, clazz);
}
}
46 changes: 46 additions & 0 deletions src/main/java/com/javarush/config/HibernateUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.javarush.config;

import com.javarush.domain.entity.City;
import com.javarush.domain.entity.Country;
import com.javarush.domain.entity.CountryLanguage;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;

import java.util.Properties;

public class HibernateUtil {
private static SessionFactory sessionFactory;

private HibernateUtil() {

}

private static SessionFactory prepareRelationalDb() {
final SessionFactory sessionFactory;
Properties properties = new Properties();
properties.put(Environment.DIALECT, "org.hibernate.dialect.MySQL8Dialect");
properties.put(Environment.DRIVER, "com.p6spy.engine.spy.P6SpyDriver");
properties.put(Environment.URL, "jdbc:p6spy:mysql://localhost:3306/world");
properties.put(Environment.USER, "root");
properties.put(Environment.PASS, "root");
properties.put(Environment.CURRENT_SESSION_CONTEXT_CLASS, "thread");
properties.put(Environment.HBM2DDL_AUTO, "validate");
properties.put(Environment.STATEMENT_BATCH_SIZE, "100");

sessionFactory = new Configuration()
.addAnnotatedClass(City.class)
.addAnnotatedClass(Country.class)
.addAnnotatedClass(CountryLanguage.class)
.addProperties(properties)
.buildSessionFactory();
return sessionFactory;
}

public static SessionFactory getSessionFactory() {
if (sessionFactory == null) {
sessionFactory = prepareRelationalDb();
}
return sessionFactory;
}
}
Loading