How can fetch reference table without using repository - spring

I want to fetch Card without using CardRepo like below
#Column(name = "account_number")
protected String accountNumber;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "account_number", referencedColumnName = "account_number", insertable = false, updatable = false)
protected Set<Card> cards;
But it doesn't work. I cannot start server.
How can I fix this?

I found out the issue. I missed to implement Serializable for that entity.
But I don't know why we need implement Serializable for enity.
However it works now.

Related

Using nested property access in a JPA repository for composite key entity implemented using #EmbeddedID approach returning null values

My SQL view :-
CREATE OR REPLACE VIEW my_view
AS
SELECT
col1,
col2,
col3,
col4,
col5,
col6
FROM
my_table
My JPA entity :-
#Entity
#Table(name = "my_view")
#Getter
#Setter
public class MyEntity {
#EmbeddedId
private MyPk myPk;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "col1", insertable = false, updatable = false)
private Entity1 entity1 ;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col2", insertable = false, updatable = false)
private Entity2 entity2;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col3", insertable = false, updatable = false)
private Entity3 entity3;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col4", insertable = false, updatable = false)
private Entity4 entity4;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col5", insertable = false, updatable = false)
private Entity5 entity5;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col6", insertable = false, updatable = false)
private Entity6 entity6;
}
My Embedded Id class:-
#NoArgsConstructor
#EqualsAndHashCode
#AllArgsConstructor
#Embeddable
public class MyPk implements Serializable {
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "col1", insertable = false, updatable = false)
private Entity1 entity1 ;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col2", insertable = false, updatable = false)
private Entity2 entity2;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col3", insertable = false, updatable = false)
private Entity3 entity3;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col4", insertable = false, updatable = false)
private Entity4 entity4;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col5", insertable = false, updatable = false)
private Entity5 entity5;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col6", insertable = false, updatable = false)
private Entity6 entity6;
}
My JPA Repository :-
public interface MyEntityRepository extends JpaRepository<MyEntity, MyPk> {
List<MyEntity> findByMyPk_Entity1_Id(Long id);
}
Entity1 has an attribute id
I have tried #IdClass approach for composite key and many other solutions related to composite key but all are giving me some kind of error during runtime
This above approach is giving me no runtime error but giving null elements when invoking findByMyPk_Entity1_Id method but its giving correct count of elements in list
I cant use any kind of sequence from table or any other unique column approach dur to my underlying code. Also I tried using #mapIds approach but its also giving null elements.
For some reason JPA repository is not able to convert into entities but its able to give correct count of entities fetched
Also when using findAll method of repository in debugger its giving me proper entities list with no null elements but when searching through nested property i am not able to get entities
spring-data-jpa:1.9.4 version
hibernate-jpa-2.1-api:1.0.0
java 8
< Its a legacy project :") >
I don't know what you really have mapped in your table/view that you really need all 6 columns to uniquely identify a row. Assuming you do, and assuming they are all references to other tables that you want to have mapped in your entity, there are a number of approaches. The easiest, IMO more flexible approach might be this:
#Entity
#Table(name = "my_view")
public class MyEntity {
#EmbeddedId
private MyPk myPk;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "col1")
#MapsId("entity1")
private Entity1 entity1 ;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col2")
#MapsId("entity1")
private Entity2 entity2;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col3")
#MapsId("entity1")
private Entity3 entity3;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col4")
#MapsId("entity1")
private Entity4 entity4;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col5")
#MapsId("entity1")
private Entity5 entity5;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col6")
#MapsId("entity1")
private Entity6 entity6;
}
#Embeddable
public class MyPk implements Serializable {
private Integer entity1 ;//use the ID type from Entity1's primary key
private Integer entity2;
private Integer entity3;
private Integer entity4;
private Integer entity5;
private Integer entity6;
}
Mappings in the MyPk embeddable class will be basic mappings, and pick up the column name settings from the entity ManyToOne mappings. Should you try to ever write one out (you can't write to views, but JPA treats everything as a table), you only need to set the appropriate relationships and JPA will extract the fk values to populate your MyEntity.myKey.entityX values for you.
Having both the embedded and the ManyToOne references means you can have the fk values within your entity and accessible without having to fetch the related entity just for the ID value.

Spring data rest ManyToMany mapping PUT/update operation is not replacing the nested object

I started to learn spring data rest. I'm doing PUT operation and it's not working for the nested objects for ManyToMany relationship, whereas it works fine for OneToMany relation.
Entities structures:
#Table(name="CONFIG_DTLS",schema = "app_txn")
#Entity
public class Config {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Column(name = "ID", updatable = false, nullable = false)
private UUID id;
#Column(name = "NAME", nullable = false, length = 75)
private String name;
/*Unable to replace the data in the MBR_CONFIG_MAPPING table in the put operation.
When the control comes to #HandleBeforeSave annotated method in PUT operation,
the request data contains the existing Member info instead of the one which i'm passing in the PUT request body */
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE},fetch = FetchType.EAGER)
#JoinTable(schema = "app_txn", name = "MBR_CONFIG_MAPPING",
joinColumns ={#JoinColumn(name="CONFIG_ID",referencedColumnName = "ID")},
inverseJoinColumns = {#JoinColumn(name="MBR_ID",referencedColumnName = "ID")}
)
private Set<Member> members;
//able to replace the notifications completely in PUT operation
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "CONFIG_ID",referencedColumnName = "ID")
private Set<Notification> notifications;
}
Member.java
#Table(name="MBR_DTLS",schema = "app_txn")
#Entity
public class Member {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Column(name = "ID", updatable = false, nullable = false)
private UUID id;
#Column(name = "OTHER_MBR_DATA", updatable = false)
private String otherMbrData;
}
Notification.java
#Table(name="NOTIFICATIONS",schema = "app_txn")
#Entity
public class Notification {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Column(name = "ID", updatable = false, nullable = false)
private UUID id;
#Column(name="LEVEL")
private String level;
#Column(name="EMAIL")
private String email;
}
Interfaces:
#RepositoryRestResource(collectionResourceRel = "configs", path="configs")
public interface ConfigRepo extends PagingAndSortingRepository<Config,UUID> {
}
#RepositoryRestResource(exported=false) // don't want to users to manipulate it directly.
public interface MemberRepo extends PagingAndSortingRepository<Member,Object> {
}
Here I don't want to add or modify anything in the MBR_DTLS table as it is loaded by another backend process. I want to update only the mapping details MBR_CONFIG_MAPPING table whenever user does the PUT/update operation. POST/create operation is working fine. Please share your thoughts on how to fix this and if you have any questions add it in the comment section.
PS: I referred some links online but that does not help much - Spring Data REST - PUT request does not work properly since v.2.5.7

Spring Boot + JPA (Hibernate) - how to update some fields

I have to update my entity - when placeA or/and placeB is updated, I have to also update points.
SO I fetch route object from database and modify one or two fields (placeA, placeB). The problem is that I have to update points accordingly -> in Point object I have to also update pointAddress (point.pointAddress has to be updated to route.placeA or route.placeB values):
public class Route{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "place_a_id")
private Address placeA;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "place_b_id")
private Address placeB;
#OneToMany(mappedBy = "route", cascade = CascadeType.ALL)
List<Point> points;
public class Point{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "point", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<PointDetail> pointDetails;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "route_id", nullable = false)
private Route route;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "route_address_id", nullable = false)
private Address pointAddress;
public class PointDetail{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "point_id", nullable = false)
private Point point;
And I fetch Route entity from db and from now this object is in persistent state (everything within the same transaction) so I don't need to invoke repository.save(myChangedRoute) explicitly.
The question is how to update route.points[0].pointAddress?
Is it sufficient?
Route route = repository.findRouteById(1L);
route.setPointA("new place");
route.getPoints().get(0).setPointAddress("new place")
And what with route.getPoints().get(0).getRoute() and route.getPoints().get(0).getPointDetails() objects? For example should I also update route.getPoints().get(0).getPointDetails() object? PointDetail objects have a field point that maybe should be also updated?
I have related (nested) objects in my Route object (dependencies) so my question is how to update my object structure properly to not overwrite new values with the old nested ones that are not updated, e.g. I updated:
route.getPoints().get(0).setPointAddress("new place")
but I haven't updated route.getPoints().get(0).getPointDetails().get(0).setPoint(MY NEW UPDATED AND NOT YET SAVED Point object)???
SO we have a circular dependencies route -> point -> pointDetail -> point and the question is if it's sufficient to update only pointAddress in my route.point object or I have to also update pointAddress in route.point.pointDetail.point?
First of all: point -> pointDetail -> point is not a curricular dependecny. The relation between pointDetail and point is BiDirectional dependency.
PointDetail objects have a field point that maybe should be also updated? Of course not.
and the question is if it's sufficient to update only pointAddress in my route.point? Yes that is enough.
Hibernate has the first level cache it ensures that objects will be loaded only once per session. So no override can occur. Of course, is up to you and your code how will you update properties and in which order.

Set of enum Spring data

I want to use in my spring application with spring data set of enums, which will be stored in db. Currently i tried it in that way:
#NotNull
#Column(name = "ROLES")
#Enumerated(EnumType.STRING)
#ElementCollection(targetClass = Role.class)
private Role role;
#NotNull
#Column(name = "PERMISSIONS")
#Enumerated(EnumType.STRING)
#ElementCollection(targetClass = Permission.class)
private Set<Permission> permissions;
but as you propably know it do not work. How can i use enums to be stored in db?
Best regards!
You should add #CollectionTable anotation with specified name and join column.
May this will work for you.
#ElementCollection(targetClass = Permission.class)
#CollectionTable(name = "permissions", joinColumns = #JoinColumn(name = "permission_id"))
#Column(name = "permission", nullable = false)
#Enumerated(EnumType.STRING)
Set<Permission> permission;

Spring Boot Data Rest POST returns 204 but only SELECTS

So this was working before I switched to Boot. Basically I was able to POST a text/uri-list to a #OneToMany resource just fine. I switched my project to use Boot and somewhere in the process it stopped working. I can PUT a text/uri-list at the #ManyToOne end, but that's not what I want to do.
When I submit the POST, I get a 204 response, but I can see the SQL on my console only Selecting and not inserting anything.
EDIT: I use Postman, but here's a curl command that does/returns the same
curl -v -X POST -H "Content-Type: text/uri-list" -d "http://localhost:8080/games/2" http://localhost:8080/developers/1/gameList
And the logger on IDEA:
Hibernate: select developer0_.developer_id as develope1_1_0_, developer0_.name as name2_1_0_ from developer developer0_ where developer0_.developer_id=?
Hibernate: select game0_.game_id as game_id1_6_0_, game0_.developer_id as develope5_6_0_, game0_.esrb_rating as esrb_rat2_6_0_, game0_.name as name3_6_0_, game0_.release_date as release_4_6_0_, developer1_.developer_id as develope1_1_1_, developer1_.name as name2_1_1_ from game game0_ left outer join developer developer1_ on game0_.developer_id=developer1_.developer_id where game0_.game_id=?
Here are my relevant classes:
#Entity
public class Developer {
#Id
#GeneratedValue
#Column(name = "developerId")
private Long id;
private String name;
#OneToMany(mappedBy = "developer", cascade = CascadeType.ALL)
private List<Game> gameList;
Other one:
#Entity
public class Game {
#Id
#GeneratedValue
#Column(name = "gameId")
private Long id;
private String name;
private Date releaseDate;
private ESRBRating esrbRating;
#ManyToMany(mappedBy = "gameList", cascade = CascadeType.ALL)
private List<User> userList;
#ManyToOne
#JoinColumn(name = "developerId")
private Developer developer;
If I'm missing any other relevant info let me know and I'll provide it.
If you want to keep it bi-directional you seem to have 2 options :
Remove the mappedBy = "developer" and let JPA use a jointable to manage the one-to-many relationship.
Developer:
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(
name="DeveloperGame",
joinColumns = #JoinColumn( name="dev_id"),
inverseJoinColumns = #JoinColumn( name="game_id")
)
private List<Game> gameList;
Game:
#ManyToOne
#JoinTable(
name="DeveloperGame",
joinColumns = #JoinColumn( name="game_id"),
inverseJoinColumns = #JoinColumn( name="dev_id")
)
private Developer developer;
Remove the mappedBy = "developer" and add a #JoinColumn if you don't want to use a jointable (make you have a joincolumn on both sides of the relationship with the same column name
Developer:
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "devId")
private List<Game> gameList;
Game:
#ManyToOne
#JoinColumn(name = "devId")
private Developer developer;
I do wonder if this is by design or if this is a bug in Spring Data REST.
Hibernate collects insert, updates and deletes until the entitymanager is flushed. This is normally done at the end of a transaction.
So it could be that your transaction management does not work correctly.
Set the logging for org.springframework.transaction to debug, than you should see when transactions are opened and closed.

Resources