Whats the JPA method to retrieve data that ignores #JoinColumn - java

So I'm new to JPA and just trying to figure out the best practices for what I need. I have code the looks like the following Entity and when I use findAll() in the controller its automatically pulling the data I want from Equipment and Validation. Whats the proper way to just retrieve Equipment though if I dont Validation. The full database is much larger than this with many more joins so there's an ask to be able to just retrieve the data within a single table also.
#Entity
#Table()
public class Equipment{
#Id
#Column(name="eqpmnt_id")
Integer equipmentId;
#Column()
String equipmentName;
#OneToOne()
#JoinColumn(name="eqpmnt_id")
Validation validation;

Related

Hibernate/JPA - Insert a field only if exists in table

I am looking for an answer if it is possible or not in hibernate. What I am trying to achieve is that if a field exists in a particular table then only it should insert it. Else just ignore the field in the #Entity class.
I want this as a new field is going to introduce in one of the table we are using and there are many dependent components which right now insert the data into that table. I don't want a big bang release. Want something like it doesn't impact the older version as well as when the upgrade happens and a new column introduced then also it should work.
For example -
#Entity
#Table(name = "EMPLOYEE_RECORDS")
public class Employee
{
#Id
#Column(name = "employee_id")
private Integer employeeId;
#Column(name = "employee_name")
private String employeeName;
#Column(name="address")
private String address;
}
What if I only want to insert address field into DB only when column(address) exists in the table EMPLOYEE_RECORDS. Please forgive me if this is something obvious, as I am not very proficient in Hibernate.
Also let me explain what have I thought of (But not sure if it will also work) -
1. Create 2 different #Entity classes. Try to insert and if the insertion failed then at runtime switch the #Entity and use without address.
2. Check if field exist in the table by simple query if it fails use #Entity without address else use without address.
I'm very confused about the scenario - It seems like there were deeper issues regarding decoupling of components in your system.
Nevertheless, you can add the column in the database, but you don't need to declare the field in the hibernate entity. On the other hand there is no way you can have an optional field in an hibernate entity. Either a field is mapped or it is not mapped.

Entity to DTO conversion increases security

I have a question to ask regarding conversion of an Entity to DTO. I have read that it is good to convert the Entity to DTO in rest application so that it hides your entity data.
For e.g
If i have an entity User and there are two rest endpoints - GET and PUT. GET fetches the data and PUT updates the data. Now if i do not convert the Entity into DTO i would expose my Entity instances and someone could use the instance with PUT method and update the User Data.
But my question is this thing can also happen if i am using the same DTO class to convert the data while using the GET and PUT method (which most developers do). So how does conversion of entity to DTO increase security.
User.java
#Entity
#Table
public class User{
private int id;
private int name;
private String password;
}
Please give you valuable comments to clarify this doubt of mine. Thanks!
You explicitly define which attributes are available to your application's peripherals. I'm not sure this increases security, but it protects you from unwanted behavior. If you automatically convert your REST bodies to JSON via Jackson, every getter of your entity would be exposed. Hence if you add a getXXX method to evaluate something in your domain, you could unwillingly expose this to the outside world.
For instance you might not want to expose which authorizations are available for your user, or the user's password.
I like to use the Adapter Pattern for REST/JMS interaction.
Separation of concerns.
GET: In your example you would not return id back to the client would you?
PUT: You would not populate the id.

Best practice of designing JPARepository(ies) for ORM Domain graph

I have been designing spring rest apis using standard MVC architecture like domain layer as POJOs and repositories to fetch domain data from db tables. So far these entities were isolated so the design acted as separate RestController, Service, and Repository flow for each entity.
I have been looking to understand the best practice when it comes to association in domain objects i.e., ORM. For example, lets take following pseudocode to illustrate domain classes (only for the purpose to express the design in question. I have not provided complete classes):
public class Customer {
#Column
private int id;
#Column;
private String name;
#OneToMany
private List<Order> orders;
//...getters setters
}
public class Order {
#Column
private int id;
#Column;
private String orderNumber;
#OneToMany
private List<Product> products;
#ManyToOne
private Customer customer;
//...getters setters
}
public class Product {
#Column
private int id;
#Column;
private String productName;
#ManyToOne
private Order order;
//...getters setters
}
The dilemma I have from designing perspective. I have following approaches that very well all be incorrect:
Define one RestController for customer and provide all the api resources like /customers, /customers/id/orders, /customers/id/orders/id/products etc. Have one Service that takes care of working with these domains. Have separate JPARepository for EACH domain. The "keep it simple" thing here is that I have separete repository for each domain so I just have to provide query methods in corresponding Repository class in order to find details for a specific domain i.e., fetch orders for a given customer Id. However, that makes me think killing the purpose of using ORM model because I am fetching individual domains through their Repository classes. This option will make all 3 repository classes wired in the service class and that also I think is not a good design. 3 might looks okay here but I have 6 to 7 domains in the ORM graph in my actual requirements so that would mean autowiring 6 repositoris in one service class.
One RestController and one Service class as in above option but the Repository class is single too. The Repository is created only for Customer domain. In this way I retrieve Customers with other domaims lazy loaded. This is to fulfil a GET request of "/customers". To fulfil a GET request of "/customers/id/orders" I will again use Customer Repository, retrieve customer for the given Id and then return list of Orders. Further, for a GET request of "/customers/id/orders/id/products" , I will require writing a manual data fetching mechanism in Customer domain so that it takes care of retrieving list of products for a given customerId and orderId. This way I use one Repository, satisfying the purpose of using ORM but then adding manual fetching data methods in Customer domain. Another negative I see is that I need to get complete list of orders in a customer domain even if I have customerId and orderId available. I would have fetched one single order based on customerId and orderId has I used a separate repository for Order.
Both are incorrect and there exists a better approach.
I have looked through spring docs for repository and hibernate docs for ORM. I went through multiple tutorials for one-to-many mappings with spring data rest but I found mixed approaches in different tutorials.
This question will look duplicate to you as I have read multiple posts on stackoverflow regarding this design concern but none of the answers give me a reasoning for the trade offs and options I mentioned above. Hence, I am reposting this question.
It is a mixed approach. e.g. in your case the product entity need not have a #ManyToOne relation with the Order. Imagine if your product is part of 1 million orders! How many time will you query a product to find orders? You will query findOrdersByProduct(Product) rather than findProductByOrder(Order)
think w.r.t your usecase. Sometimes it makes sense to have one directional mapping if you will never fetch the information other than from the owner of the relationship
Think about the amount of data that you will fetch (including the joins) if you query an entity.
e.g if i am fetching an organization do i need to fetch all its employees? your system will go for a toss (lazy loading will save you most of the time but if you have an Angular then it will bind and fetch the entire model). But it does make sense to have many to one relationship with an org from the employee entity.

How does Hibernate work with normalized databases?

Preliminary Info
I'm currently trying to integrate Hibernate with my team at work. We primarily do Java web development, creating webapps that provide data to clients. Our old approach involves calling stored procedures with JDBC (on top of Oracle boxes) and storing their results in beans. However, I've heard a lot about the benefits of integrating Hibernate into a development environment like ours so I'm attempting to move away from our old habits. Note: I'm using the Hibernate JPA annotation approach due to simplicity for team adoption's sake.
Specific Problem
The specific issue I'm having currently is using Hibernate with normalized tables. We have a lot of schemas structured like so:
StateCodes (integer state_code, varchar state_name)
Businesses (integer business_id, varchar business_name, integer state_code)
I want to be able to have a single #Entity that has all of the "Businesses" fields, except instead of "state_code" it has "state_name". To my understanding, Hibernate treats #Entity classes as tables. The #OneToMany, #OneToOne, #ManyToOne annotations create relationships between entities, but this is a very simplistic, dictionary-like lookup and I feel like it doesn't apply here (or might be overkill).
One approach I've seen is
#Formula("(select state_name from StateCodes where Businesses.state_code = state_code)")
private String stateCode;
But, given Hibernate's perk of "avoiding writing raw SQL", this seems like bad practice. Not to mention, I'm extremely confused about how Hibernate will then treat this field. Does it get saved on a save operation? It's just defined as a query, not a column, after all.
So what is the best way to accomplish this?
I do not see any reason not use the standard JPA mappings in this case. Short of creating a database view and mapping an entity to that (or using the non-JPA compliant #Formula) then you will have to map as below.
Unless you are providing a means for the State to be changed then you do not need to expose the State entity to the outside world: JPA providers do not need getters/setters to be present.. Neither do you need to Map a State to Businesses:
#Entity
#Table(name = "Businesses")
public class Business{
//define id and other fields
#ManyToOne
#JoinColumn(name = "state_code")
private State state;
public String getStateName(){
return state.getName();
}
}
#Entity
#Table(name="StateCodes")
public class State{
//define id and other fields.
#Column(name = "state_name")
private String stateName;
public String getStateName(){
return stateName;
}
}

How to inject custom object ids into JPA entities

I am using JPA 2 for an enterprise application, and my DBA's just hit me with a twist.
They want me to use the group's centralized object ID generator for all my tables. This means rather than using table values or a sequence table, I will need to call a web service to get a batch of ~50 ids.
Then, as I persist any new object, I would need to inject this id first, and save that to the table.
So how would I manipulate the #Id column of an entity to handle this.
Is it as simple as setting a key before I persist? I suspect that would throw some sort of unmanaged entity with ID set error.
Update:
The better method is to actually specify a Sequence strategy on Generated fields and specify a custom Sequence class.
JPA will then call this class's nextId() method every time it inserts a new object.
This method allows full graphs to be persisted without intervening on each entity manually.
Figured it out. Amazingly complex ;) - just remove the GeneratedValue annotation from the key field.
It is intended for Native Ids like SSN or email, but works regardless of source.
#Entity
public class Client{
#Id
#Column(name="CLNT_ID")
private long key;
#Column(name="CLNT_NUM")
private String clientNumber;
...
}

Categories