I am working on a Java 8 project with an Oracle database. The project uses Spring Boot and JPA. Currently, the JPA Entity is defined in the following manner
#Entity
#Table(name = "TEST")
public class TestEntity {
#Column(name = "ID", columnDefinition = "NVARCHAR2(140)")
private String id;
#Column(name = "COMMON_FIELD_1", columnDefinition = "NVARCHAR2(60)")
private String commonField1;
#Column(name = "COMMON_FIELD_2", columnDefinition = "NVARCHAR2(60)")
private String commonField2;
#Column(name = "UNCOMMON_FIELD_1", columnDefinition = "NVARCHAR2(60)")
private String uncommonField1;
#Column(name = "UNCOMMON_FIELD_2", columnDefinition = "NVARCHAR2(60)")
private String uncommonField2;
}
Here - the fields id, commonField1, and commonField2 are present for every database entry and every instance of a TestEntity. The fields uncommonField1 and uncommonField2 will not be present in every database entry (null when not present) and will also not be present in every instance of TestEntity.
Although the code functionally does know which uncommon fields to expect in any given instance of TestEntity, it is bad practice to have those fields accessible across every instance of TestEntity. I would like to encapsulate these fields in such a way that only those fields which the code expects to be present are actually accessible.
Here is an example of what I would like to accomplish:
#Entity
#Table(name = "TEST")
public abstract class TestEntity {
#Column(name = "ID", columnDefinition = "NVARCHAR2(140)")
private String id;
#Column(name = "COMMON_FIELD_1", columnDefinition = "NVARCHAR2(60)")
private String commonField1;
#Column(name = "COMMON_FIELD_2", columnDefinition = "NVARCHAR2(60)")
private String commonField2;
}
public class TestEntityA extends TestEntity {
#Column(name = "UNCOMMON_FIELD_1", columnDefinition = "NVARCHAR2(60)")
private String uncommonField1;
}
public class TestEntityB extends TestEntity {
#Column(name = "UNCOMMON_FIELD_2", columnDefinition = "NVARCHAR2(60)")
private String uncommonField2;
}
Is it possible to implement something like what I've described above? Can my database table contain all 5 columns?
Related
I want to fetch data from subclass via Query Method in spring data jpa. not want to use jpql or criteria api. I want to get all Connections with specified accountType and user. I know jpql as well as criteria api will do the job but i want to achieve this with JPA Query Method. Below is my code of entities and repos.
This is main entity class
#Entity
public class Connection {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(
name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator"
)
#Column(name = "Connection_Id", nullable = false, updatable = false)
private String id;
#Column(name = "User_Id", nullable = false)
private String userId;
#Column(name = "Name", nullable = false, length = 64)
private String name;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "Connection_Type_Id")
private ConnectionType connectionType;
// default constructor
// getter and setter
}
this is abstract and concrete entity classes
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class ConnectionType {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(
name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator"
)
#Column(name = "Id", nullable = false)
private String id;
#OneToOne(mappedBy = "connectionType")
private Connection connection;
// default constructor
// getter setter
}
#Entity
public class EmailAccount extends ConnectionType {
#Column(name = "Username", nullable = false)
private String username;
#Column(name = "Password", nullable = false)
private String password;
#Column(name = "Account_Type", nullable = false)
#Enumerated(value = EnumType.STRING)
private AccountType accountType;
#Column(name = "FullName")
private String fullName;
// default constructor
// getter setter
}
#Entity
public class OtehrAccount extends ConnectionType {
#Column(name = "Client_Id", nullable = false)
private String clientId;
#Column(name = "Client_Secret", nullable = false)
private String secret;
// default constructor
// getter and setter
}
below is repository
public interface ConnectionRepository extends JpaRepository<Connection, String> {
// Query Method that i want to write but is not valid.
List<Connection> findByUserIdAndconnectionTypeAccountType(String userId, AccountType accountType);
}
findByUserIdAndconnectionTypeAccountType method in repo is incorrect so i just want to know weather or not is it possible to write method that will get me result based on subclass property?
Just to clarify, "JPA Query Method" is actually "Spring Data JPA Query Methods", so this is not a Hibernate question. In JPQL/HQL this kind of subtype access usually works by using the TREAT operator, or in case of HQL, it also works implicitly. Since the Spring Data JPA documentation does not mention the treat operator, I would say it's not possible: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation
findByUserIdAndconnectionTypeAccountType Means You Want To Fetch A Connection
By userId And The AccountType Where Is Field Of Entity ConnectionType .
Class ConnectionType Have No Attribute With Name AccountType That You Want To Get A Connection By AccountType.
AccountType Is An Attribute Of A Subclass Of Class ConnectionType.
So The connectionTypeAccountType Is Incorrect.
AccountType Is Attribute Of Entity EmailAccount Wher Is Extending ConnectioType, You Canot Get A Connection With AccountType
I'm working on a project using Spring Data JPA and Hibernate and one of my entities has a pretty weird behaviour: it's not saving some of the properties. The code below is my model class. It has some more properties and but all of them are String with the columnDefinition="TEXT". Debugging the object I'm sending to the repository all properties have values in it, get and set methods are working fine, etc.. but it only saves the title.
Now comes the weird part, if I do this Column(name = "isbn_v", columnDefinition="TEXT") it saves the data normally. The same for all columns that are not saving. I don't understand why. Model class:
#Entity
#Table(name = "notice")
public class Notice implements Serializable {
private static final long serialVersionUID = 4521230269805147556L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "seq_no", nullable = false, updatable = false)
private Integer id;
#Column(name = "title", columnDefinition="TEXT")
private String title;
#Column(name = "isbn", columnDefinition="TEXT")
private String isbn;
#Column(name = "issn", columnDefinition="TEXT")
private String issn;
#Column(name = "author", columnDefinition="TEXT")
private String author;
#Column(name = "publisher", columnDefinition="TEXT")
private String publisher;
// get / set
}
Repository class:
public interface NoticeRepository extends JpaRepository<Notice, Integer>, JpaSpecificationExecutor<Notice> {
#Override
#Modifying
#Transactional
Notice save(Notice notice);
}
Any clue of what I'm doing wrong?
I am developing Spring Boot (2.1.7.RELEASE) +Data Jpa + Postgres example. In this example I am explicitly passing EMP_ID value=100 and next I am allowing data-jpa to automatically take next Id which is 101. I am not sure why its not working in that way??
Employee.java
#Data
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true)
#Entity
public class Employee extends BaseEntity{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "EMP_ID", unique = true, nullable = false)
private Integer empId;
#Column(name = "EMP_NAME", unique = true, nullable = false)
private String empName;
#Column(name = "EMP_EMAIL", unique = true, nullable = false)
private String empEmail;
#Builder(builderMethodName="eBuilder")
public Employee(Integer empId, String empName, String empEmail,
Instant createdDate, Instant lastUpdateDate,String createUser, String lastUpdateUser) {
super(createdDate, lastUpdateDate, createUser, lastUpdateUser);
this.empId = empId;
this.empName = empName;
this.empEmail = empEmail;
}
}
BaseEntity.java
#Data
#MappedSuperclass
#NoArgsConstructor
#AllArgsConstructor
#EntityListeners(AuditingEntityListener.class)
public class BaseEntity {
#CreatedDate
#Column(name = "createdDate", nullable = false, updatable = false)
private Instant createdDate;
#Column(name = "lastUpdateDate", nullable = false)
#LastModifiedDate
private Instant lastUpdateDate;
#Column(name = "createUser", nullable = false, length = 50)
private String createUser;
#Column(name = "lastUpdateUser", length = 50)
private String lastUpdateUser;
}
MainApp.java
#SpringBootApplication
#EnableJpaAuditing
public class MyExampleApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(MyExampleApplication.class, args);
}
#Autowired
private EmployeeRepository employeeRepository;
#Override
public void run(String... args) throws Exception {
Employee e = Employee.eBuilder().empId(100).empName("Shrutika")
.empEmail("shrutika#hotmail.com")
.createUser("Shrutika")
.lastUpdateUser("Shrutika")
.build();
employeeRepository.save(e);
Employee e1 = Employee.eBuilder().empName("Shantaram")
.empEmail("shantaram#hotmail.com")
.createUser("Shantaram")
.lastUpdateUser("Shantaram")
.build();
employeeRepository.save(e1);
}
}
Even if I used below, still things doesn't works well
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "emp_generator")
#SequenceGenerator(name="emp_generator", sequenceName = "emp_seq", allocationSize=1)
#Column(name = "EMP_ID", unique = true, nullable = false)
private Integer empId;
Spring JIRA: https://jira.spring.io/browse/DATAJPA-1588
Ensure the type EMP_ID on the database: SERIAL or Integer. For using IDENTITY with postgres it has to be SERIAL (https://www.postgresql.org/docs/8.1/datatype.html#DATATYPE-SERIAL).
I am explicitly passing EMP_ID value=100 and next I am allowing data-jpa to automatically take next Id which is 101. I am not sure why its not working in that way??
JB Nizet answered that in the comments:
a sequence generator consists in getting the next ID from a database sequence. Not in getting the next ID from the ID you last inserted yourself.
I always wanted to save records by always doing max id +1. Is there any way of doing this with Spring Data JPA
Again JB Nizet pointed out this is a terrible idea.
It would require a lock on the or at least the index for every insert including the select to determine the next id.
So: DON'T DO THIS
If you still want to do it Vlad Mihalcea describes how to implement a custom id generator.
This should allow you to implement your own generator.
Of course this is Hibernate specific.
This will work:
#GeneratedValue(strategy=GenerationType.SEQUENCE)
I'm just learning Spring Data. I want to map a database view Entity with a simple Entity and pass to DTO which will contain columns both entities. I understand that I can use a special database view but I need to map precisely entities of Spring Data.
I have a database view Entity "MentorStudents":
#Entity
#Table(name = "mentor_students")
#Immutable
public class MentorStudents implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "mentor_id", updatable = false, nullable = false)
private Long mentorId;
//This entity I need to map
private Mentor mentor;
#Column(name = "active_students")
private Integer activeStudents;
public MentorStudents() {
}
//getters, setters, equals, hashCode
}
A database view sql of an above entity is:
SELECT id AS mentor_id, active_students
FROM mentor
LEFT JOIN ( SELECT mentor_id, count(mentor_id) AS active_students
FROM contract
WHERE close_type IS NULL
GROUP BY mentor_id) active ON mentor.id = active.mentor_id
ORDER BY mentor.id;
And I have a simple Entity "Mentor":
#Entity
#Table(name = "mentor")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Mentor implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#NotNull
#Column(name = "first_name", nullable = false)
private String firstName;
#NotNull
#Column(name = "last_name", nullable = false)
private String lastName;
#Column(name = "patronymic")
private String patronymic;
#Column(name = "phone")
private String phone;
#NotNull
#Column(name = "email", nullable = false)
private String email;
#Column(name = "skype")
private String skype;
#Column(name = "country")
private String country;
#Column(name = "city")
private String city;
#Column(name = "max_students")
private Long maxStudents;
//getters, setters, equals, hashCode
I have to get a DTO which contains all Mentor fields and an "activeStudents" MentorStudents field without a "mentorId" field. How do it?
Use spring data projection:
public interface YourDto {
// all Mentor get fields
String getFirstName();
...
// activeStudents get field
Integer getActiveStudents();
}
public interface YourRepository extends JpaRepository<YourEntity, Integer> {
#Query(value = "select ...(all fields match YourDto) from Mentor m, MentorStudents s where m.id = s.mentorId and m.id = ?1")
Optional<YourDto> findMyDto(Integer mentorId);
}
I need two Ebean model classes called "States" and "Children". A "State" object can contain nested Child objects(List of children).
Here is the basic States class,
#Entity
public class States extends Model {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Constraints.Required(message = "stateName cannot be null")
#Column(nullable = false)
private String statename;
#Column(nullable = true)
private String url;
#Column(nullable = true)
private String parent;
private List<Children> childrenList;
}
Here is the basic Children class,
#Entity
public class Children extends Model {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(nullable = false)
private String statename;
#Column
private String child;
}
What are the minimal modifications that should be done to these classes to create State objects using Ebean ORM? I went through the post,
Ebean Query by OneToMany Relationship
But there, a lot of changes have been suggested. I just want the minimal modifications.
All I had to do was, doing a small modification to the "States" class,
#Entity
public class States extends Model {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Constraints.Required(message = "stateName cannot be null")
#Column(nullable = false)
private String statename;
#Column(nullable = true)
private String url;
#Column(nullable = true)
private String parent;
#OneToMany(cascade = CascadeType.ALL)
private List<Children> childrenList;
}
Only change I have done here is,
#OneToMany(cascade = CascadeType.ALL)
I did not do any changes to the "Children" class. Before starting the play app I set
play.evolutions.enabled = true
in "application.conf" file. Then using the evolution SQL file that was created in "evolution.default" folder, I adjusted the schema of the database. After that "States" objects were created successfully with nested "Children" objects.