Take data from postgresql and write it to mongodb - spring

I am new to using nosql databases, specifically mongodb.
My project is based on spring data jpa (postgresql). I would like to know if it is possible to use posgresql and mongodb together? I would like to take data from a postgresql database and write it to mongodb so as not to load postgre.
My Entity for mongodb
#Data
#Document(collection = "exposure")
public class Exposure {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#NotNull
private Facility facility;
#ManyToOne
#NotNull
private Investee investee;
#ManyToOne
#NotNull
private Investee investeeName;
#ManyToOne
#NotNull
private Tranche tranche;
}
Repository
#Repository
public interface ExposureRepository extends MongoRepository<Exposure, Long> {
}
DtoService
#Service
#RequiredArgsConstructor
public class DefaultExplosureDtoService implements ExplosureDtoService {
private final FacilityService facilityService;
private final InvesteeService investeeService;
private final ExplosureMapper explosureMapper;
private final ExplosureService explosureService;
#Override
#Transactional
public ExplosureDto create(CreateExplosureDto explosureDto) {
Explosure explosure = new Explosure();
if (explosureDto.getFacilityId() != null) {
explosure.setFacility(facilityService.getById(explosureDto.getFacilityId()));
}
if (explosureDto.getInvesteeId() != null) {
explosure.setInvestee(investeeService.getById(explosureDto.getInvesteeId()));
}
Explosure savedExplosure = explosureService.save(explosure);
return explosureMapper.toExplosureDto(savedExplosure);
}
}
Service
#Service
#RequiredArgsConstructor
public class DefaultExplosureService implements ExplosureService {
private final ExplosureRepository explosureRepository;
#Override
public Explosure save(Explosure explosure) {
return explosureRepository.save(explosure);
}
}
When I run the application, I get a 405 error. And I can’t figure out how to take data from the postgres database and write it to mongodb

Related

JPA issue (lazy loading? eager ?)

Iam building a simple Spring Boot app, with 2 entities:
- Student model
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Student {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String password;
private boolean active;
private Date dob;
private String roles;
#ManyToOne
private Training training;
}
- Training model
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Training {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int duration;
#OneToMany(mappedBy = "training")
#JsonIgnore
private Collection<Student> students;
}
EDIT
I run the app by adding 2 resources in the db:
public static void main(String[] args) {
SpringApplication.run(MsSchoolingSbApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
Training t1=trainingRepo.save(new Training(null,"php", 20, null));
Training t2=trainingRepo.save(new Training(null,"java", 20, null));
Student st=new Student(null, "XXXX", "ZZZZ", true,new Date(),"ADMIN",t1);
Student st2=new Student(null, "XXXXX2", "ZZZZZ2", true,new Date(),"USER",t2);
studentRepo.save(st);
studentRepo.save(st2);
}
END EDIT
EDIT 2
- StudentRepo
#RepositoryRestController
public interface StudentRepo extends JpaRepository<Student, Long>{
public List<Student> findByNameStartsWith(String name);
Optional<Student> findByName(String name);
}
- TrainingRepo
#RepositoryRestController
public interface TrainingRepo extends JpaRepository<Training, Long> {
}
END EDIT 2
i've tried to put fetch = FetchType.EAGER or LAZY, i've also added #JsonIgnore but as soon as i fill the db with new data (trainings and students) and run the app, i get this message:
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.schooling.models.Training.students, could not initialize proxy - no Session
What am i doing wrong ?
The problem you got must have related to how you use those 2 entities so you need to provide more information about how you use it.
You might want to look out for your problem in this tutorial: https://www.baeldung.com/hibernate-initialize-proxy-exception
Do not use Lombok's #Data annotation on #Entity classes.
Reason: #Data generates hashcode(), equals() and toString() methods that use the generated getters. Using the getter means of course fetching new data even if the property was marked with FetchType=LAZY.
Somewhere along the way hibernate tries to log the data with toString() and it crashes
EDIT
you can exclude the relation from the toString method by adding, for example in my case:
#ToString(exclude = {"students"})

Get records for last 3 days via Spring JPA Repository

I have an entity which contains field date.
#Entity
#Table(name="messages", schema = "users")
...
public class Message {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "author")
private String author;
#Column(name = "tags")
private String tags;
#Column(name = "message_date")
private LocalDate date;
#Override
public String toString() {
...
}
}
#Repository
public interface MessageRepository extends JpaRepository<Message, Long> {
Message findByMessageId(Long id);
}
I'm using Spring Data JPA with repository. I want to get all messages from database for last 3 days (field date). How can I do it with Spring JPA?
#Query(...?)
List<Message> findBy...?
I suggest to split the logic from the actual queries. A service could handle all the intermediate things, e.g.:
#Service
public class MessageService {
private final MessageRepository repository;
#Autowired
public MessageService(MessageRepository repository) {
this.repository = repository;
}
List<Message> getLastThreeDays() {
// subtract 3 days from today
LocalDate threeDaysAgoDate = LocalDate.now().minusDays(3);
return this.repository.findAllWithDateAfter(threeDaysAgoDate);
}
}
and your repository stays nice and clean:
#Repository
public interface MessageRepository extends JpaRepository<Message, Long> {
Optional<Message> findByMessageId(Long id);
#Query("select m from Message m where date >= :threeDaysAgoDate")
List<Message> findAllWithDateAfter(#Param("threeDaysAgoDate") LocalDate threeDaysAgoDate);
}

When does the hibernate session gets closed

I have created the following entities.
#Entity
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToMany(mappedBy = "student")
private List<Book> books;
}
#Entity
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToOne
#JoinColumn(name = "STUDENT_ID")
private Student student;
}
My controller looks like this
#RestController
public class Controller {
MyService myService;
public Controller(MyService myService) {
this.myService = myService;
}
#GetMapping("student")
public List<Book> getBooksForStudent(Long id) {
return myService.getBooks(id);
}
}
The service is as follows.
public class MyService {
#Autowired
private StudentRepo studentRepo;
public List<Book> getStudent(Long id) {
Optional<Student> studentOptional = studentRepo.findById(id);
return studentOptional.map(Student::getBooks).orElseThrow(IllegalArgumentException::new);
}
}
I am getting the list of books as expected. But as I'm having lazy loaded list for books I should be getting a LazyInitializationException. I have not added transnational to the method and I'm returning the list of books from the entity itself without mapping it to a DTO. Why is the hibernate session not getting closed after the end of the method?
#RestController is transactional by default. Spring boot automatically registers an OpenEntityManagerInViewInterceptor when you use a web application/you use JPA. Refer #RestController methods seem to be Transactional by default, Why?

Limiting the visibility of results from the database on one page- spring boot

I create web application in spring boot using the postgress database.
I want to limit the number of records per page(now it's 30,000 records - it's loading a long time), so what should i do to limit it? I use thymeleaf.
Model:
#Entity(name="articles")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Articles {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long article_id;
private String title;
private String issn;
private String eissn;
private String title2;
private String issn2;
private String eissn2;
private Integer points;
#ManyToMany
#JoinTable(
name = "articles_categories",
joinColumns = #JoinColumn(name = "article_id"),
inverseJoinColumns = #JoinColumn(name = "category_id"))
private List<Category> categories;
....
getters and setters
Repository:
public interface ArticlesRepository extends JpaRepository<Articles,Long> {
}
Controller:
#Controller
#RequestMapping("/articles")
public class ArticlesController {
private ArticleService articleService;
#Autowired
public void setArticleService(ArticleService articleService) {
this.articleService = articleService;
}
#GetMapping
public String getAll(Model model)
{
model.addAttribute("articles", articleService.list());
return "articles";
}
Service:
#Service
public class ArticleService {
#Autowired
private ArticlesRepository articlesRepository;
public ArticleService() {
}
public List<Articles> list(){
return articlesRepository.findAll();
}}
Use Pageable to limit the size of your articles.
public List<Articles> list(int page, int limit){
Page<Articles> pageableArticales = articlesRepository.findAll(PageRequest.of(page, limit);
return pageableArticales.getContent();
}
Note that repository.findAll(pageable) wraps the list of data on Page, which provides getNumber(), getSize(), getNumberOfElements(), getTotalPages(), getTotalElements, etc.
And consider exploring PageRequest and PagedResources as well.

How can I add a tenant condition to Spring Data JPA Default and Dervied Queries

I have a Springboot Application with Repositories having Spring Data JPA Queries like findOne, findAll and also derived ones like findByID or findByName etc.
What I want to achieve is multitenancy. All entities have an "account_id" column which holds the tenant.
How do I add a filter like "account_id" to all the queries metioned above without using derived queries that contains those name slike findIdAndAccountid (which would be findone)
#Repository
public interface CategoryRepository extends JpaRepository<Category, Long> {
Category findByName(String name);
}
Here's the corresponding entity
#Entity
#Table(name = "unit")
#Data
public class Unit {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
#Column(name = "account_id")
private Long account_id;
}
I know most people use schemas as tenant separation but that's impossible for me. Is there a way (I didn't find one) to add such a tenant filter condition on those queries without writing NamedQueries or using DerivedQueries. An elegeant solution like annotate the repository or entity or maybe the queries that all queries should add the additional filter "account_id"?
You can add Where clause on your Entity classes (Didnt had time to test )
#Entity
#Table(name = "unit")
#Data
#Where(clause = "account_id= :account_id")
public class Unit {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
#Column(name = "account_id")
private Long account_id;
}
Update and Solution
1. Create a Filter & FilterDef on the entity like so
#FilterDef(name="accountFilter", parameters=#ParamDef( name="accountId", type="long" ) )
#Filters( {
#Filter(name="accountFilter", condition=":accountId = account_id")
} )
public class Category {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
#Column(name = "account_id")
private Long account_id;
}
enable filtering in the controller by autowiring entitymanager, writing a method to enable the filter and activate the filter in #ModelAttribute for each request
#RestController
#RequestMapping(path = "/categories",produces = MediaType.APPLICATION_JSON_VALUE )
public class CategoryController {
private final CategoryRepository repository;
#Autowired
private EntityManager entityManager;
CategoryController(CategoryRepository repository) {
this.repository = repository;
}
private void activateFilter() {
Session session = entityManager.unwrap(Session.class);
Filter filter = session.enableFilter("accountFilter");
filter.setParameter("accountId", Long.valueOf(TenantContext.getCurrentTenant()));
}
#ModelAttribute
public void initFilter() {
activateFilter();
}
... your rest methods here
}

Resources