Embeddable with #ManyToOne and AccessType.Property on Id field - spring

I am trying to define a #Embeddable class with a #ManyToOne relationship to another entity. The #OneToMany side has a #Id field annotated with #Access(AccessType.Property). Here is an example code to illustrate:
#Embeddable
class Embeddable {
#ManyToOne
private ClassA classA;
}
#Entity
class ClassA implements Serializable {
#Id
#GeneratedValue
#Access(AccessType.Property)
private Long id;
public Long getId() { return id; }
}
#Entity
class ClassB {
#ElementCollection
private List<Embeddable> embeddables;
}
The problem is that the created table definition for the class Embeddable seems to be wrong. The column for ClassA is of type bytea and is named class_a instead of bigint and class_a_id. Also if i try to persist one dataset the related entity of ClassA is saved serialized as hex..Every other #Entity works as expected. Is this a bug or an error on my part?
Edit:
Table for Embeddable (current state):
| class_b_id | class_a |
| type: int | type: typea |
|:-----------|------------:|
| 123 | A3B4738D.. |
Table for Embeddable (needed state):
| class_b_id | class_a_id |
| type: int | type: int |
|:-----------|------------:|
| 123 | 2 |

Related

Reverse Map Foreign Key in Spring JPA

I have two tables
Table1
I have two tables
Cat
Id
Name
Owner_Id
Owner
Id
Name
#Entity
#Table(name="cat")
class Cat {
#Id
private Long id;
private Sting Name;
#JoinColumn(name = "owner_id")
#OneToOne
private Onwner owner;
}
#Entity
#Table(name="owner")
class Owner {
#Id
private Long id;
private Sting Name;
}
Now when fetching cat I will have the Owner object. My question is is it possible to get the field cat added in Owner so that when querying owner will get Cat also.
class Owner {
#Id
private Long id;
private Sting Name;
private Cat cat; /// Like this
}
You definitely can - also I suggest reading up about different relationship types in the documentation - lots of different annotations to use.
For a bidirectional relationship, you need to also outline the OneToOne relationship on your Owner entity - changing the join parameter to mappedBy
#OneToOne(mappedBy = "owner")
private Cat cat;

Composite primary key vs multiple primary keys

Having this entities:
User.java:
#Entity
#NoArgsConstructor
#Getter
#Setter
public class User {
#Id
private int id;
private String username;
#OneToMany(mappedBy = "owner")
#MapKey(name = "friend_id")
private Map<User, Friendship> friends = new HashMap<>();
}
Friendship:
#Entity
#Data
//#IdClass(Friendship.class)
public class Friendship implements Serializable {
#Id
private int owner_id;
#Id
private int friend_id;
private String level;
#ManyToOne
#MapsId("owner_id")
private User owner;
#ManyToOne
#MapsId("friend_id")
private User friend;
}
I though I must have #IdClass or #EmbeddedId if I want to use two or more primary keys. But as shown above, I could ommit either, and just declare two primary keys (this is what I mean it "compiles"). So the question is, why to even bother using either of those annotations and just declare more keys?
generated table:
+-----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| owner_id | int | NO | PRI | NULL | |
| friend_id | int | NO | PRI | NULL | |
| level | varchar(255) | YES | | NULL | |
+-----------+--------------+------+-----+---------+-------+
As it's mentioned in the hibernate documentation:
The restriction that a composite identifier has to be represented by a "primary key class" (e.g. #EmbeddedId or #IdClass) is only JPA-specific.
Hibernate does allow composite identifiers to be defined without a "primary key class" via multiple #Id attributes.
Although the mapping is much simpler than using an #EmbeddedId or an #IdClass, there’s no separation between the entity instance and the actual identifier. To query this entity, an instance of the entity itself must be supplied to the persistence context.
#Entity
public class Friendship implements Serializable {
/*
It's better to use object wrapper classes instead of the corresponding
primitive types. Because, for example, uninitialized Integer is null,
but uninitialized int is 0 that can be a legal id.
*/
#Id
private Integer ownerId;
#Id
private Integer friendId;
public Friendship() {
}
public Friendship(Integer ownerId, Integer friendId) {
this.ownerId = ownerId;
this.friendId = friendId;
}
// ...
}
Friendship friendship = entityManager.find(Friendship.class, new Friendship(ownerId, friendId));

Implementing State management for a Spring boot application

I am working on a Spring boot application, where I need to implement a state transition table in the following format, where the combination of action and condition determines the transition of the entity from one state to another state:
From State| To State| User Action | Condition
----------------------------------------------------------------
S0 | S1 | Create a record |
-----------------------------------------------------------------
S1 | S1 | Update a record |
-----------------------------------------------------------------
S1 | S1 | Run validation |
-----------------------------------------------------------------
S1 | S2 | Mark state as S2 | Record is valid
In this application, there are already APIs for Create, Update, and Running Validation.
As per the requirement, the state machine should be configurable, so that any new state can be added/existing state can be deleted as well as action and condition can be changed for a given transition through the dashboard.
To accomodate this requirement, I have thought of considering State, Action, Condition and StateTransition as separate entities as follows:
Action entity:
#Entity
#Table(name = "action")
public class Action implements Serializable {
#Id
private String id;
#Column(name = "action_type")
#Enumerated(EnumType.STRING)
private ActionType actionType;
#Column(name = "action_description")
private String actionDescription;
}
Action type enum:
Enum ActionType {
CREATE_RECORD, UPDATE_RECORD, RUN_VALIDATION;
}
State entity:
#Entity
#Table(name = "state")
public class State implements Serializable {
#Id
private String id;
#Column(name = "name")
private String name;
}
Condition entity (Cannot understand how to do this)
#Entity
#Table(name = "condition")
public class Condition implements Serializable {
#Id
private String id;
#Column(name = "description")
private String description;
... CANNOT UNDERSTAND WHAT ELSE SHOULD I PUT HERE
}
State Transition entity as follows:
#Entity
#Table(name = "state_transition")
public class State implements Serializable {
#Id
private String id;
#Column(name = "from_state")
private String fromStateId;
#Column(name = "to_state")
private String toStateId;
#Column(name = "action_id")
private String actionId;
#Column(name = "condition_id")
private String conditionId;
}
Basically, with the combination of current state, action, and condition, I should be able to figure out the next state from the state transition table.
But I am stuck in the following things:
How should I implement the User Action? As I mentioned before, some of these actions as I specified above have been there in the application as APIs. Basically, I cannot understand how can I tie the user action entity that I would like to implement with the existing flow.
How should I implement the Condition as an entity ? Condition is basically an expression that either evaluates to True/False. I am not getting any clue as, how can I capture this as an entity.
Could anyone please give any pointer regarding this? Thanks.
I don't see a reason for introducing all these entities. Action seems to be static i.e. you are defining the possible actions in code, so why bother and create an entity for that? Condition is just a field in the Transition entity containing an expression. I also don't think you need to have a dedicated entity for State, but that might be debatable. Overall, I would suggest you serialize the state machine as JSON though because you will always read the entire state machine anyway. So unless you want to be able to query or refer to dedicated elements in that definition (referential integrity), don't model this as tables. So either
#Entity
#Table(name = "state_transition")
public class StateTransition implements Serializable {
#Id
private String id;
#Column(name = "from_state")
private String fromState;
#Column(name = "to_state")
private String toState;
#Column(name = "action")
private ActionType action;
#Column(name = "condition")
private String condition;
}
or
#Entity
#Table(name = "state_machine")
public class StateMachine implements Serializable {
#Id
private String id;
#Column(name = "definition")
private String definition;
}
You can then de-serialize the definition or even do that in an AttributeConverter to whatever model that suits your needs.

spring JPA repository not flushing record to database

I see the response from repository.save() as updated object but same is not getting reflected in database.
I tried using JPArepository also and tried saveandflush method exposed by API but no luck.
Below are the details of code and configuration
Used sql driver with hibernate as middleware.
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=
spring.datasource.url=jdbc:mysql://localhost:3306/ccmBatch?useSSL=false
spring.batch.job.enabled=false
spring.datasource.initialize=false
spring.jpa.open-in-view=false
spring.jpa.hibernate.ddl-auto=create
spring.jpa.generate-ddl=true
spring.jpa.hibernate.use-new-id-generator-mappings=true
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.hibernate.naming.strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
POJO :
#Entity(name = "jobtracker")
public class JobTracker implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Column(name = "CCMBATCH")
String lockVal;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
long id;
public JobTracker() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getLockVal() {
return lockVal;
}
public void setLockVal(String lockVal) {
this.lockVal = lockVal;
}
}
Repository :
#Repository
public interface JobTrackerRepository extends CrudRepository<JobTracker, Long>{
}
Caller code :
#Autowired
JobTrackerRepository repsositoy;
#Transactional
public boolean acquireJobExecutionLock() {
LOGGER.info("acquire lock!!!");
String jobExecutionLockValue = new Date().toString();
JobTracker tracker = new JobTracker();
tracker.setLockVal(jobExecutionLockValue);
JobTracker job = repsositoy.save(tracker);
LOGGER.info("lock acquired!!!"+job.id+" "+job.lockVal);
return true;
}
job object used above is printing correct updated values.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.4.3</version>
</dependency>
mysql> desc jobtracker;
+----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| id | bigint | NO | PRI | NULL | |
| CCMBATCH | varchar(255) | YES | | NULL | |
+----------+--------------+------+-----+---------+-------+

Unable to persist child class records using spring data jpa

Problem
1)I created a class employee that extends Person.I can persist employee records and not person.
Solution I implemented
1)Created modal classes Employee and Person.
Modal Classes
Employee
#Entity
#Table(name = "employee",
indexes = {#Index(name = "username", columnList = "username", unique = true)})
public class BaseEmployee extends Person {
public static final String OBJECT_KEY = "EMPLOYEE";
#Id
#GeneratedValue
private Long id;
// Who created this baseEmployee
private String userId;
// #Indexed(unique = true)
private String username;
#Enumerated(EnumType.STRING)
private ROLE role;
#Enumerated(EnumType.STRING)
private STATUS status;
private String designation;
Person
public class Person extends AbstractEntity {
private String firstName;
private String lastName;
private String contact;
private String email;
#Enumerated(EnumType.STRING)
private GENDER gender;
private String imageId;
private String address;
private String locationId;
private DateTime dob;
private double[] location;
private String pinCode;
Employee Controller
#PostMapping
ApiResponse<BaseEmployee> post(#RequestBody CreateEntry<BaseEmployee> baseEmployeeCreateEntry) {
BaseEmployee baseEmployeeToCreate = baseEmployeeCreateEntry.getEntry();
baseEmployeeToCreate.setStatus(STATUS.ACTIVE);
baseEmployeeToCreate = employeeService.post(baseEmployeeToCreate);
if (baseEmployeeToCreate != null)
authenticationService.setPassword(baseEmployeeToCreate, baseEmployeeCreateEntry.getPassword());
return ApiResponse.success().message("Created Successfully!").object(baseEmployeeToCreate);
}
public class CreateEntry<T> {
private T entry;
private String password;
Service
#Override
public <E extends BaseEmployee> E post(E employee) {
employee = employeeRepository.save(employee);
System.out.println(employee);
LOG.info("Admin data" + employee);
LOG.info("PUT employee {} {} {}", employee.getId(), employee.getFirstName(), employee.getEmail());
return employee;
}
Repository
public interface EmployeeRepository extends JpaRepository<BaseEmployee, Long> {}
#Expected Output
The Employee class Columns and Person class Columns
#Output Shown
mysql> select * from employee;
id | created_at | created_by | last_modified_at | last_modified_by | version | designation | role | status | user_id | username |
1 | 1519624346694 | anonymousUser | 1519624346694 | anonymousUser | 0 | csdcs | ADMIN | ACTIVE | string | admin |
3 | 1519624856504 | anonymousUser | 1519624856504 | anonymousUser | 0 | csdcs | ADMIN | ACTIVE | string | admin1 |
4 | 1519626598478 | anonymousUser | 1519626598478 | anonymousUser | 0 | csdcs | ADMIN | ACTIVE | string | admin2 |
Can anyone guide me what I am doing wrong.Thankx in advance?
Can you try adding #MappedSuperclass to Person entity?

Resources