what is the best way to design the skeleton of springboot application - java

I am writing a springboot application that should calculate the grade of a student. You should read the name of the student from command line, and call an API that will return the student ID in JSON. Then I will take the Student ID and call another API that would return the total of the student Marks in JSON. Then I should calculate the grade accordingly. The calculation is as follow: F=<60, D=<65, C=< 75, B=<85, A=<100.
Below is my hypothetical view of how I will write the project, I think I know how to write the code that will function, but I am stuck with what is the best skeleton for the project or design of classes. Below is how I think it should be written.
#Controller
// Rest call to the Student API
public class StudentController {
private static final Logger log = LoggerFactory.getLogger(ClientController.class);
private RestOperations rest;
private Config config; //to read the URL
.....
#Controller
// Rest call to the grades API
public class GradsController
{
private static final Logger log = LoggerFactory.getLogger(ClientController.class);
private RestOperations rest;
private Config config; //to read the URL
.....
Configuration
#ConfigurationProperties(prefix="api.call")
public class Config {
private String URL;
.....
public class Student {
#JsonProperty("Data")
private Elements[] elements;
.......
public class Grads {
#JsonProperty("Subjects")
private String[] subjects;
.......
#Service
public class CalculateGrade {
private String marks;
private HashMap <String, Integer> gradeMap;
CalculateGrade( String marks)
{
this.marks = marks;
this.carName = carName;
fillMap();
}
private void fillMap()
{
gradeMap= new HashMap<>();
gradeMap.put("A",100);
gradeMap.put("B", 85 );
..........
}
public String getGrade()
{
// do calculations;
}
#SpringBootApplication
#ConfigurationPropertiesScan ("Config")
public class MyApplication {
private static final Logger log = LoggerFactory.getLogger(MyApplication.class);
#Autowired StudentController StudentController;
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}

For a skeleton, it looks quite sensible. Controllers to translate from Web to Domain and the business logic in domain/application services (CalculateGrade). Looks a lot like the Hexagonal architecture ("Ports&Adapters").

Related

Values not getting picked up from application.properties

I'm trying to access values from my application.properties file in the Neo4jConnectionFactory class. I am able to access the properties within my Setup beans class. But, I want to be able to access them from Neo4jConnectionFactory. When I try to do so I get null values. Is this possible?
Applications.properties
neo4j.url = bolt://localhost:7687
neo4j.user = neo4j
neo4j.password = neo4j
SetupBeans.java
#Component
public class SetupBeans {
#Bean
public Neo4jClient neo4jClient() {
return new Neo4jConnectionFactory().build();
}
}
Neo4jConnectionFactory.java
#Configuration("neo4j")
public class Neo4jConnectionFactory {
#Value("${neo4j.url}")
private String url;
#Value("${neo4j.user}")
private String user;
#Value("${neo4j.password}")
private String password;
public Neo4jClient build() {
return new Neo4jClient(getUrl(), getUser(), getPassword());
}
}
MyApplication.java
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
I think this automatically prepends the prefix neo4j
#Configuration("neo4j")
So your #Value statments should omit the prefix, like this:
#Value("${url}")
private String url;
#Value("${user}")
private String user;
#Value("${password}")
private String password;

How do I pass in populated list as a parameter in an object constructor?

I have a java class that I am setting up to store LoL champions in a project also using lombok (thus the lack of getters and setters) and Spring. The class looks like this:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Champion {
#Id
private String champName;
private String role;
private List<String> type; // <--- how do I populate this?
private String phrase;
private List<Champion> counterTo; //<--- or this?
private List<Champion> counteredBy;// <-- you get the idea.
}
I'm overriding the run() method from spring using commandLineRunner in my main application class but I don't know how to populate the lists when I invoke the constructor using Spring's .save() method when persisting into my MongoDB. Can anyone help?
Here is the main class below for context:
public class PlaygroundApplication implements CommandLineRunner {
#Autowired ChampionRepository repo;
public static void main(String[] args) {
SpringApplication.run(PlaygroundApplication.class, args);
}
#Override
public void run(String...args){
repo.deleteAll();
//repo.save("CHAMPION_ENTITY_GOES_HERE);
}
}
If I understand you correctly, you can try new a object and set the param it or Custom constructor

Issue with interface java/spring

I wrote this code
#Controller
public class StudentsController {
private ArrayList<student> students = new ArrayList<>();
private IStudentsRepository studentsRepository;
#GetMapping("/")
public String index(Model model){
model.addAttribute("students", students);
return "index";
}
#GetMapping("/create")
public String create(#ModelAttribute student student) {
studentsRepository.create(student);
return "create";
}
.....
}
for a web application and I get this error java.lang.NullPointerException: null
at com.example.demo.Controller.StudentsController.create(StudentsController.java:27) ~[classes/:na]
When I try to access the page create with its functionalities it doesn't work just gives me that error what is the problem? Also when I initialize the IStudent Repository interface IntelliJ tells me that studentsRepository is not assigned even if I used it to create method.
You are not injecting IStudentsRepository. Use #Autowired
You can use Field Injection like this
#Autowired
private IStudentsRepository studentsRepository;
Or the best way would be to use Constructor Injection.
#Controller
public class StudentsController {
private ArrayList<student> students = new ArrayList<>();
private final IStudentsRepository studentsRepository;
#Autowired
public StudentsController(final IStudentsRepository studentsRepository) {
this.studentsRepository = studentsRepository;
}

How to connect Spring with MySQL database?

I have a simple project, based on this guide. I created a simple REST interface and I want it to use my database. I added Hibernate to the dependencies and created the DAO class. I'm using Spring Tool-Suite for IDE. As far as I understand I should add some beans to tell the classes what to use but I don't understand how. Here are my classes.
Application.java
package com.learnspring.projectfirst;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Marker.java
package com.learnspring.projectfirst;
#Entity
public class Marker {
#Id
#Column
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column
private double longitude;
#Column
private double latitude;
#Column
private String address;
public Marker() {
// Empty constructor
}
public Marker(long id, double longitude, double latitude, String address) {
this.id = id;
this.longitude = longitude;
this.latitude = latitude;
this.address = address;
}
//Getters and Setters
}
MarkerController.java
package com.learnspring.projectfirst.controller;
#Controller
public class MarkerController {
private Logger logger = Logger.getLogger(MarkerController.class.getName());
#Autowired
private MarkerServiceImplementation markerService;
#RequestMapping(value="/markers", method=RequestMethod.GET)
public #ResponseBody List<Marker> getMarkers(#RequestParam(value="city", defaultValue="") String city) {
return this.markerService.getAllMarkers();
}
#RequestMapping(value="/markers/new", method=RequestMethod.POST)
public #ResponseBody Marker addMarker(#RequestBody Marker marker) {
this.markerService.addMarker(marker);
return marker;
}
}
MarkerDaoImplementation.java
package com.learnspring.projectfirst.dao;
#Repository
public class MarkerDaoImplementation implements MarkerDaoInterface {
#Autowired
private SessionFactory sessionFactory;
#Override
public void addMarker(Marker marker) {
this.sessionFactory.getCurrentSession().save(marker);
}
#Override
public void deleteMarker(int markerId) {
this.sessionFactory.getCurrentSession().delete(this.getMarker(markerId));
}
#Override
public Marker getMarker(int markerId) {
return (Marker) this.sessionFactory.getCurrentSession().get(Marker.class, markerId);
}
#Override
public List<Marker> getAllMarkers() {
return this.sessionFactory.getCurrentSession().createQuery("from Marker").list();
}
}
MarkerServiceImplementation.java
package com.learnspring.projectfirst.service;
#Service
public class MarkerServiceImplementation implements MarkerServiceInterface {
#Autowired
private MarkerDaoImplementation markerDao;
#Transactional
public void addMarker(Marker marker) {
this.markerDao.addMarker(marker);
}
#Transactional
public void deleteMarker(int markerId) {
this.markerDao.deleteMarker(markerId);
}
#Transactional
public Marker getMarker(int markerId) {
return this.markerDao.getMarker(markerId);
}
#Transactional
public List<Marker> getAllMarkers() {
return this.markerDao.getAllMarkers();
}
}
And here is the file structure:
I understand that I should tell my program the database name and the columns using beans but I don't understand how. How can I link the java code to the beans? Sorry I pasted so much code, I just wanted to make sure you have everything needed. Thank you in advance!
This is the one you need: Spring Boot with MySQL
Refer this example : Spring MVC with JdbcTemplate Example
The annotations in your "Marker" class determine the MySQL table and column names (based on the class and class variable names). The tablename will be "marker", with the columns "id", "longitude", "latitude", "address".
You forgot the most important part in your code: your spring configuration. it determines how the SessionFactory instance will be initialized before being injected into your DAO class. Here you have to set an appropriate connection to the MySQL Server (e.g. via an JNDI Resource)

How To Configure MongoDb Collection Name For a Class in Spring Data

I have a collection called Products in my MongoDB database, which is represented by the interface IProductPrice in my Java code. The following repository declaration causes Spring Date to look to the collection db.collection: Intelliprice.iProductPrice.
I want it to configure it to look in db.collection: Intelliprice.Products using an external configuration rather than putting an #Collection(..) annotation on IProductPrice. Is this possible? How can I do this?
public interface ProductsRepository extends
MongoRepository<IProductPrice, String> {
}
The only way you can currently achieve this is by annotating your domain class with #Document using the collection property to define the name of the collection instances of this class shall be persisted to.
However, there's a JIRA issue open that suggests adding a pluggable naming strategy to configure the ways class, collection and property names are handled in a more global way. Feel free to comment your use case and vote it up.
using answer from Oliver Gierke above,
working on a project where I need to create multiple collections for one entity, I wanted to use the spring repositories and needed to specify the entity to use before using the repository.
I managed to modify the repository collection name on demand using this system, it using SPeL. You can only work on 1 collection at a time though.
Domain object
#Document(collection = "#{personRepository.getCollectionName()}")
public class Person{}
Default Spring Repository:
public interface PersonRepository
extends MongoRepository<Person, String>, PersonRepositoryCustom{
}
Custom Repository Interface:
public interface PersonRepositoryCustom {
String getCollectionName();
void setCollectionName(String collectionName);
}
implementation:
public class PersonRepositoryImpl implements PersonRepositoryCustom {
private static String collectionName = "Person";
#Override
public String getCollectionName() {
return collectionName;
}
#Override
public void setCollectionName(String collectionName) {
this.collectionName = collectionName;
}
}
To use it:
#Autowired
PersonRepository personRepository;
public void testRetrievePeopleFrom2SeparateCollectionsWithSpringRepo(){
List<Person> people = new ArrayList<>();
personRepository.setCollectionName("collectionA");
people.addAll(personRepository.findAll());
personDocumentRepository.setCollectionName("collectionB");
people.addAll(personRepository.findAll());
Assert.assertEquals(4, people.size());
}
Otherwise if you need to use configuration variables, you could maybe use something like this? source
#Value("#{systemProperties['pop3.port'] ?: 25}")
A little late,
but I've found you can set the mongo collection name dynamically in spring-boot accessing the application configuration directly.
#Document(collection = "#{#environment.getProperty('configuration.property.key')}")
public class DomainModel {...}
I suspect you can set any annotation attribute this way.
The only comment I can add is that you have to add # prefix to the bean name:
collection = "#{#beanName.method()}"
for the bean factory to inject the bean:
#Document(collection = "#{#configRepositoryCustom.getCollectionName()}")
public class Config {
}
I struggled to figure it out..
COMPLETE EXAMPLE:
#Document(collection = "#{#configRepositoryCustom.getCollectionName()}")
public class Config implements Serializable {
#Id
private String uuid;
private String profile;
private String domain;
private String label;
private Map<String, Object> data;
// get/set
}
public interface ConfigRepositoryCustom {
String getCollectionName();
void setCollectionName(String collectionName);
}
#Component("configRepositoryCustom")
public class ConfigRepositoryCustomImpl implements ConfigRepositoryCustom {
private static String collectionName = "config";
#Override
public String getCollectionName() {
return collectionName;
}
#Override
public void setCollectionName(String collectionName) {
this.collectionName = collectionName;
}
}
#Repository("configurations")
public interface ConfigurationRepository extends MongoRepository<Config, String>, ConfigRepositoryCustom {
public Optional<Config> findOneByUuid(String Uuid);
public Optional<Config> findOneByProfileAndDomain(String profile, String domain);
}
usage in serviceImpl:
#Service
public class ConfigrationServiceImpl implements ConfigrationService {
#Autowired
private ConfigRepositoryCustom configRepositoryCustom;
#Override
public Config create(Config configuration) {
configRepositoryCustom.setCollectionName( configuration.getDomain() ); // set the collection name that comes in my example in class member 'domain'
Config configDB = configurationRepository.save(configuration);
return configDB;
}
I use static class and method in SpEL;
public class CollectionNameHolder {
private static final ThreadLocal<String> collectionNameThreadLocal = new ThreadLocal<>();
public static String get(){
String collectionName = collectionNameThreadLocal.get();
if(collectionName == null){
collectionName = DataCenterApiConstant.APP_WECHAT_DOCTOR_PATIENT_COLLECTION_NAME;
collectionNameThreadLocal.set(collectionName);
}
return collectionName;
}
public static void set(String collectionName){
collectionNameThreadLocal.set(collectionName);
}
public static void reset(){
collectionNameThreadLocal.remove();
}
}
In Entity class ,#Document(collection = "#{T(com.test.data.CollectionNameHolder).get()}")
And then ,use
CollectionNameHolder.set("testx_"+pageNum)
in Service , and
CollectionNameHolder.reset();
Hope it helps you.

Categories