I am searching for a design solution to the problem where I have 2 classes which depend on each other such that I have class Customer and class Order where:
Customer can have a list of orders (1-to-N) and an Order has a designated customer (1-to-1).
What is the best practice to break these kind of dependencies?
Assuming you have a dependency as follows:
public class Customer {
private long customerId;
private String name;
private String address1;
// ....
private List<Order> orders;
}
public class Order {
private long orderNumber;
private Date orderDate;
// ... others
private Customer customer;
}
You could create a third class to break the dependency:
public class CustomerOrder {
private final Customer customer;
private final List<Order> orders;
public CustomerOrder(Customer customer) {
super();
this.customer = customer;
this.orders = new ArrayList<Order>();
}
public void addOrder(Order order) {
orders.add(order);
}
public Customer getCustomer() {
return customer;
}
public List<Order> getOrders() {
return orders;
}
}
Now you can drop orders from the Customer class, and customer from the Order class. Or am I misunderstanding your issue?
As a software engineer for approx 2 years, the best I’ve seen for a case like that is to put a shadow definition of one class, without initializing it to anything, simply telling the compiler “hey orders exist”, then defining the other class explicitly, followed by your orders class explicitly. Does that get you in the right direction? Nodes and trees sometimes are modeled this way, and data structure and analysis of algorithms books tend to have decent design solutions to this too.
Related
Let's assume an application where there are leagues and teams inside of leagues, and teams can be in multiple leagues aswell. So we do have a many to many relationship.
League Entity
#Data
#Entity
public class League {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String countryCode;
private SportType sportType;
#ManyToMany(mappedBy = "leagues")
private List<Team> teams;
}
Team Entity
#Data
#Entity
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String logoUrl;
private SportType sportType;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "team_league",
joinColumns = #JoinColumn(name = "team_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "league_id", referencedColumnName = "id"))
private List<League> leagues;
}
I am now on the point where i need for example to create a new team, and upon creation, it needs a league to put in, which has to already exist. That means i need an endpoint which takes a list of leagueIds instead of a List<League>. So i assume i need to build a DTO. But how should the DTO look like and how would i implement the method that maps the DTO to an entity and saves it to the database.
My idea of the TeamDTO
#Data
public class TeamDTO {
private Long id;
private String name;
private String logoUrl;
private SportType sportType;
private List<Integer> leagueIds;
}
So instead of a List<League> i do have a List<Integer> leagueIds so that the endpoint can accept proper JSON. Is that correct?
Now i want to create the team in the database, IF the leagues of List<Integer> leagueIds are present in the database. So my question now is, when do i map to the entity.
My idea of the implementation of the service
public class TeamServiceImpl implements TeamService {
#Autowired
private LeagueRepository leagueRepository;
#Autowired
private TeamRepository teamRepository;
#Override
public Team createTeam(TeamDTO teamDTO) {
List<Long> ids =
teamDTO.getLeagueIds().stream().filter(leagueId ->
leagueRepository.findById(leagueId).isPresent()).
collect(Collectors.toList();
if (!ids.isEmpty()) {
Team team = new Team();
team.setName(teamDTO.getName());
team.setLogoUrl(teamDTO.getLogoUrl());
team.setSportType(teamDTO.getSportType());
// do i actually need the League entities to set this?
team.setLeagues(...);
return team;
}
return null;
}
}
Most important question is: Is this the correct way?
Should i use a mapper for DTO to entity and vice versa?
Should i implement a mapper myself (i mean it only maps a few
fields)?
And on what place i would use the mapper, if i would implement one?
I don't know why you only want to save the team if it has leagues assigned that exist. It just sounds wrong to me i.e. some kind of bug is in your app if the league for an id does not exist. You should set a list of league references and rely on the FK-constraint to error if a wrong league id is used i.e. use something like this:
List<League> leagues =
teamDTO.getLeagueIds().stream().map(leagueId ->
leagueRepository.getOne(leagueId)).
collect(Collectors.toList());
team.setLeagues(leagues);
The DTO approach is fine and as long as it stays this simple, I guess using this custom implementation is good enough. If you have more complex requirements and want to make use of more efficient processing I would recommend you look into Blaze-Persistence Entity-Views which was made for exactly this purpose, efficient mapping between JPA entities and DTOs.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Team.class)
#UpdatableEntityView
public interface TeamDTO {
#IdMapping
Long getId();
String getName();
void setName(String name);
String getLogoUrl();
void setLogoUrl(String logoUrl);
SportType getSportType();
void setSportType(SportType sportType);
#UpdatableMapping
#JsonIgnore
List<LeagueDto> getLeagues();
default List<Long> getLeagueIds() {
return getLeagues().stream().map(LeagueDto::getId).collect(toList());
}
default void setLeagueIds(List<Long> ids) {
getLeagues().clear();
ids.stream().map(id -> evm().getReference(LeagueDto.class, id)).forEach(getLeagues()::add);
}
// This is a special context providing method
EntityViewManager evm();
#EntityView(League.class)
interface LeagueDto {
#IdMapping
Long getId();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
TeamDTO a = entityViewManager.find(entityManager, TeamDTO.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<TeamDTO> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
The saving part will then be as simple as this:
public class TeamServiceImpl implements TeamService {
#Autowired
private TeamRepository teamRepository;
#Override
public Team createTeam(TeamDTO teamDTO) {
teamRepository.save(teamDTO);
return teamRepository.getOne(teamDTO.getId());
}
}
Due to the change-tracking implementation of Entity-Views, at any point in time it is clear what is dirty and will by default only flush these changes and avoid unnecessary select statements during flushing.
Animal.java
#Data
#Entity
public class Animal implements MyEntityInterface {
public enum Sex {MALE, FEMALE}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private Sex sex;
private boolean castrated;
#OneToMany
private List<Symptom> symptoms;
}
AnimalDTO.java
#Getter
#Setter
public class AnimalDTO implements Serializable {
private long id;
private String name;
private Animal.Sex sex;
private boolean castrated;
private List<Long> symptoms;
}
I wish for a list of Symptoms to be automatically mapped to a list of ID's. This could be achieved in many ways, such as creating a TypeMap, creating a Converter or even just by creating a method in AnimalDTO.java:
public void setSymptoms(List<Symptom> symptoms) {
if (symptoms != null)
this.symptoms = symptoms.stream().map(s -> s.getId()).collect(Collectors.toList());
}
But now imagine it's not only Symptoms, but 50 other fields too. That's a lot of code for the same functionality. And then, it's not only Animal to AnimalDTO, but another 30 different classes with their respective DTOs too.
Also, that still leaves the way back open. From ID to entity. This can (in theory) be achieved easily with the following pseudocode:
List<EntityMemberField.class> list;
for (var entityid : listOfEntityIDsOfDto) {
Object persistedObject = entityManager.find(EntityMemberField.class, entityid);
list.add(persistedObject);
}
...
ModelMapperDestination.setField(list);
This is the same for absolutely every Entity/DTO and should automatically happen for every Entity relationship where the Entity implements MyEntityInterface.
An idea how I could achieve that would be overriding MappingEngineImpl.java from ModelMapper which I register as a Spring Service and inject the EntityManager into, but how could I get ModelMapper to use mine? Or is there maybe an easier way?
The goal is to have a fairly automated conversion from Spring Entities to their corresponding DTO by... just calling modelMapper.map(entity, EntityDTO.class);
I want to model my database tables as classes. In my database, I have many to many relationship as below, and I want to get ideas how to model it correctly.
.
These are the classes that I have tried to create for these three tables.
class Product {
......
private List<Price> priceList;
......
}
class Price{
.....
private List<Product> products;
.....
}
I think this way can help me model those tables correctly, but I lost the fields - price_list.amount.
If you want two classes, simply join the Price List table with either Price or Product. For example:
public class PricePoint
{
private int priceId;
private int priceListId;
private String description;
private Date createdOn;
private boolean status;
private int amount;
private List<Product> products;
}
public class Product
{
private List<PricePoint> pricePoints;
//...
}
So i have following Entitys/Tables for a many to many relation: Satz, Track and the mapping Table Trackliste
#Entity
class Track{
// name, id
#ManyToMany(targetEntity = Satz.class, fetch = FetchType.LAZY)
#JoinTable(
name="trackliste", joinColumns=#JoinColumn(name="TrackID"),
inverseJoinColumns=#JoinColumn(name="SatzID"))
private Set<Satz> saetze;
// getters and setters
}
#Entity
class Trackliste {
// id, trackid, satzid.
// getters and setters
}
#Entity
public class Satz implements Serializable {
// id, titel, werkId, etc
#ManyToMany(mappedBy="saetze", fetch = FetchType.LAZY)
private Set<Track> tracks;
// getters and setters
}
and my repository looks like this:
public interface SatzRepository extends CrudRepository<Satz, Integer> {
List<Satz> findById(int id);
List<Satz> findByWerkId(int id);
//Some query maybe?
//List<Satz> findByTracks(String name);?
}
The Mapping works so far, when i call my Webservice it returns a json object and with the help of debugging i can see that the SatzRepository Set Contains objects of Track.
Now comes my question: How do i return a Satz based on the given Track name is this possible? Lets say i have a URL like this: localhost:8080/rest/satz/trackname?name=%trackname%
If you need more Information please tell me.
you can add a method in TrackRepository to find track by name then you can get satz list from track object.
public interface TrackRepository extends CrudRepository<Track, Integer> {
Track findByName(String name);
}
#Transactional
public TrackServiceImpl implement TrackService{
#AutoWired
TrackRepository trackRepository;
List<Satz> getSatzByTrackName(String name){
Track track = trackRepository.getByName(name);
return track != null ? track.getSaetze() : new ArrayList<>();
}
}
My application works with 2 databases: 1 SQL Server and 1 PostgreSQL.
The SQL Server is used for the website and had some performance issues in the past, therefore, I separated some analysis data into a different database, where I am adding data constantly and calculate some things all day long.
In most of the cases I use the databases separately, but in one case I would like to join two tables from different databases.
Is there any way I can do this with Hibernate?
To exemplify, I have this two simplified entities: Order and OrderSource.
The Order Table is in the SQL Server and OrderSource is in the PostgreSQL.
They both work fine by themselves, but when I added the OrderSource to the entity Order and Order to the entity OrderSource, it obviously went wrong, because they are trying to access a database that is configured in another SessionFactory.
Order Entity:
#Entity
#Table(name=DomainConstants.TB_ORDER, schema=DomainConstants.DB_SCHEMA)
public class Order {
#Id
#Column(name="cod")
#GeneratedValue
private Long id;
#OneToOne(mappedBy="order", fetch=FetchType.LAZY)
private OrderSource orderSource;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public OrderSource getOrderSource() {
return orderSource;
}
public void setOrderSource(OrderSource orderSource) {
this.orderSource = orderSource;
}
}
OrderSource Entity:
#Entity
#Table(name=DomainConstants.TB_ORDER_SOURCE, schema=DomainConstants.DB_SCHEMA_GOOGLE)
public class OrderSource {
#Id
#Column(name="id")
private long id;
#Column(name="source")
private String source;
#Column(name="medium")
private String medium;
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="id")
private Order order;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getMedium() {
return medium;
}
public void setMedium(String medium) {
this.medium = medium;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
One solution I could come up with was to work on my OrderService with my OrderDao and my OrderSourceDao.
This way I removed the mapping from each of the entities and created one Service to get all Orders and iterate over all of them getting the OrderSource.
This is, however, not as fast as I would like and I am not able to group data or order them.
#Component
public class OrderServiceImpl implements OrderService {
private OrderDao orderDao;
private OrderSourceDao orderSourceDao;
#Autowired
public OrderServiceImpl(OrderDao orderDao, OrderSourceDao orderSourceDao) {
this.orderDao = orderDao;
this.orderSourceDao = orderSourceDao;
}
public List<Order> getOrdersWithOrderSource() {
List<Order> orders = this.orderDao.index(); // get all Orders
for(Order order: orders)
order.setOrderSource(this.orderSourceDao.findById(order.id)); // Find OrderSource by Order id
}
}
Has anyone come across something like this? Does anyone have a solution for this join on different databases?
Thanks so much everyone!
Have you considered using an SQL query to join the tables over the DB link and then via ResultSetHandler map it to the entities (if it's just for view purposes) or based on this retrieve set of IDs in the DB where you want to make the change and then select the entities through HQL and ID set so you could store them to your main DB (I am assuming your main DB where you would want to change the data is the web one and not the analytical one)?