I am getting a null pointer exception when i try to call a getter for a relationship. it is my understanding that the container will fill this field in with an appropriate list object whilst the entity is still managed.
the schema was pre-existing so this is a bottom up mapping.
This is my 'one' side entity of the onetomany relationship:
#Entity
#Table(name=CollectorHeader.TABLE_NAME)
public class CollectorHeader implements Serializable {
...
#Id
#Column(name = "COLLECTORHEADERID")
private long id;
#OneToMany(mappedBy="collectorHeader", fetch=FetchType.LAZY)
private List<CollectorDetail> collectorDetails;
...
}
And here is my 'many' side entity :
#Entity
#Table(name = CollectorDetail.TABLE_NAME)
public class CollectorDetail implements Serializable {
...
#Id
#Column(name = "COLLECTORDETAILID", unique = true)
long id;
...
#NotNull
#ManyToOne
#JoinColumn(name = "COLLECTORHEADERID")
private CollectorHeader collectorHeader;
...
public CollectorDetail(long id, #NotNull CollectorHeader collectorHeader,
long provenanceLinkPk, #NotNull String provenanceLinkClass) {
setId(id);
setCollectorHeader(collectorHeader);
setProvenanceLinkPk(provenanceLinkPk);
setProvenanceLinkClass(provenanceLinkClass);
}
}
And this is where i am calling the relationship:
public CollectorDetail createCollectorDetail(long collectorHeaderId, long provenanceLinkPk, #NotNull String provenanceLinkClass) throws SystemException {
CollectorHeader collectorHeader = em.find(CollectorHeader.class, collectorHeaderId);
if(collectorHeader == null) {
String error = "There is no Collector Header with the id: '" + collectorHeaderId + "'";
log.error(error);
throw new SystemException(error);
}
CollectorDetail collectorDetail =
new CollectorDetail(NextNumberFactory.getInstance().getNextNumberLong("cocollectordetail")
, collectorHeader
, provenanceLinkPk
, provenanceLinkClass);
collectorHeader.getCollectorDetails().add(collectorDetail); //NULLPOINTEREXCEPTION
em.merge(collectorDetail);
em.merge(collectorHeader);
return collectorDetail;
}
Sql Schema:
CREATE TABLE COCOLLECTORHEADER (
COLLECTORHEADERID DECIMAL(20,0) NOT NULL PRIMARY KEY,
COLLECTEDTIMESTAMP TIMESTAMP, -- the date when the information was collected
PROCESSEDTIMESTAMP TIMESTAMP, -- the date when the information was processed
FILEFORMAT VARCHAR(16) NOT NULL,
SEQUENCENUMBER INTEGER
);
CREATE TABLE COCOLLECTORDETAIL (
COLLECTORDETAILID DECIMAL(20,0) NOT NULL PRIMARY KEY,
COLLECTORHEADERID DECIMAL(20,0) NOT NULL
REFERENCES COCOLLECTORHEADER(COLLECTORHEADERID),
PROVENANCELINKPK DECIMAL(20,0) NOT NULL,
PROVENANCELINKCLASS VARCHAR(128) NOT NULL
);
Any assistance would be much appreciated.
Initialize collectorDetails with a valid collection object as:
private List<CollectorDetail> collectorDetails = new ArrayList<CollectorDetail>();
Also check Is it good practice to initialize fields inside a JPA entity getter?
Related
Im trying to find one object from my DB table Real_States which goes like this:
CREATE TABLE REAL_STATES (
address VARCHAR(30) NOT NULL,
admin_id VARCHAR(15) NOT NULL,
resident_id VARCHAR(15),
real_state_type_id INT(6) NOT NULL,
block VARCHAR(3) NOT NULL,
internal_id INT(5) NOT NULL,
PRIMARY KEY (address, block, internal_id),
FOREIGN KEY (real_state_type_id) REFERENCES REAL_STATE_TYPES (real_state_type_id),
FOREIGN KEY (admin_id) REFERENCES ADMINS (admin_id),
FOREIGN KEY (resident_id) REFERENCES RESIDENTS (resident_id)
);
And I want to get a row of this table by its composite Primary Key (address, block, internal_id), all this from my EntityManager. This goes like this:
public RealState findRealState(RealStateID realStateId) {
RealState realState = em.find(RealState.class, realStateId); // first Try, which failed
List<RealState> realStates = em.createQuery("FROM RealState rs WHERE rs.realStateID.address like :" + realStateId.getAddress()).getResultList(); // second try which failed too
for (RealState realState2 : realStates) {
System.out.println(realState2.toString());
}
if (realState == null) {
throw new EntityNotFoundException("Can't find realState for ID " + realStateId.toString());
}
return realState;
}
The RealStateID Class is declared like this:
#Embeddable
public class RealStateID implements Serializable{
private static final long serialVersionUID = 6485406412363395170L;
#Column(name = "address")
private String address;
#Column(name = "block")
private String block;
#Column(name = "internal_id")
private int internal_id;
//getters..setters..
But Im just getting error by error, IDK what I am doing wrong, and I would like to figure out it. Im not using Spring, just JPA Hibernate and Java.
As it's stated in the documentation:
The primary key class must define equals and hashCode methods, consistent with equality for the underlying database types to which the primary key is mapped.
Assuming it, the method:
RealState realState = em.find(RealState.class, new RealStateID(...));
should work as expected.
EDIT Your RealStateID should look like this:
#Embeddable
public class RealStateID implements Serializable{
private static final long serialVersionUID = 6485406412363395170L;
#Column(name = "address")
private String address;
#Column(name = "block")
private String block;
#Column(name = "internal_id")
private int internal_id;`
// getters, setters
#Override
public boolean equals(Object o) {
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
RealStateID pk = (RealStateID) o;
return Objects.equals(address, pk.address) &&
Objects.equals(block, pk.block) &&
Objects.equals(internal_id, pk.internal_id);
}
#Override
public int hashCode() {
return Objects.hash(address, block, internal_id);
}
}
I'm working on adding a feature to an already developed spring boot web application. The primary entity that has child entities is a Record. It has a few columns/variables that I want to now be in its own, separate entity (CustomerOrder) and exist in a one-to-one relationship with the Record. To summarize:
Record {
thing 1
thing 2
thing 3
}
is now becoming:
CustomerOrder {
thing 1
thing 2
thing 3
}
Record {
CustomerOrder
}
I'm having some issues with what I've produced. Here is the CustomerOrder model's relevant relationship data:
#Entity
#Table(name="customer_orders")
public class CustomerOrder {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
... other columns
#OneToOne(orphanRemoval = true, cascade = CascadeType.ALL, mappedBy="customerOrder", fetch = FetchType.EAGER)
private Record record;
}
And then here is the Record model's relevant data:
#Entity
#Table(name="records")
public class Record extends Auditable<String> implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
... other columns
#OneToOne
#JoinColumn(name="customer_order_id", nullable = false, unique = true)
private CustomerOrder customerOrder;
}
My issue exists when I try to POST a record, when a user tries creating one in the ui. Here is the POST method for a record:
#PostMapping
public ResponseEntity<?> saveRecord(#RequestBody Record recordBody, BindingResult result) {
if(!result.hasErrors()) {
if(recordBody.getHardwareItems().isEmpty()) {
record = recordsService.save(recordBody);
} else {
// Save the record first, recordId is required on hardwareItems
// TODO: investigate Spring Hibernate/JPA rules - is there a way to save parent before children to avoid a null recordId
CustomerOrder customerOrder = recordBody.getCustomerOrder();
recordBody.setCustomerOrder(new CustomerOrder());
customerOrder.setRecord(record);
customerOrder = customerOrdersService.save(customerOrder);
record = recordsService.save(recordBody);
}
} else {
return new ResponseEntity<>(result.getAllErrors(), HttpStatus.BAD_REQUEST);
}
// Return the location of the created resource
uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{recordId}").buildAndExpand(record.getId()).toUri();
return new ResponseEntity<>(uri, HttpStatus.CREATED);
}
The error I receive is the following:
2021-02-19 00:46:28.398 WARN 29990 --- [io-8080-exec-10] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1364, SQLState: HY000
2021-02-19 00:46:28.398 ERROR 29990 --- [io-8080-exec-10] o.h.engine.jdbc.spi.SqlExceptionHelper : Field 'record_id' doesn't have a default value
This makes sense to me at least, since I'm trying to save the CustomerOrder object that depends on a Record object, which has yet to have been persisted. So, how do I go about changing up the order and/or creating and persisting a Record object so that I can then save the CustomerOrder object to it?
You need to mark your column record_id as AI(AUTO_INCREMENT) in your table definition.
ALTER TABLE records CHANGE record_id INT(6) NOT NULL AUTO_INCREMENT;
Your primary key is record_id, add #Column(name = "record_id", nullable = false)
#Entity
#Table(name="records")
public class Record extends Auditable<String> implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "record_id", nullable = false)
private Long id;
... other columns
#OneToOne
#JoinColumn(name="customer_order_id", nullable = false, unique = true)
private CustomerOrder customerOrder;
}
I have a legacy app which has an entity relationship that looks like this. I changed the names of the fields to less realistic values and reduced to only the relevant fields.
CREATE TABLE `billing_target` (
`billingTargetID` int(11) NOT NULL,
`targetType` char(5) NOT NULL,
`targetID` int(11) NOT NULL
PRIMARY KEY (`billingTargetID`)
);
CREATE TABLE `Client` (
`clientID` int(11) NOT NULL,
`name` varchar(200),
`color` varchar(200),
`shape` varchar(200)
PRIMARY KEY (`clientID`),
CONSTRAINT `fk_client_billingTarget`
FOREIGN KEY (`clientID`)
REFERENCES `billing_target` (`targetID`)
);
My most current attempt which causes an issue when saving as it gives a null entity key exception.
#Data
#Entity
public class BillingTarget implements Serializable {
#Id
#Column(name = "billingTargetID")
private Integer id;
#Column(name = "targetID", nullable = false)
private Integer targetID;
#Column(name = "targetType", nullable = false)
private String type;
}
#Data
#Entity
public class Client implements Serializable {
#Id
#Column(name = "clientID")
private Integer id;
#OneToOne
#JoinColumn(name = "clientID")
#MapsId("targetID")
private BillingTarget billingTarget;
private String name;
private String color;
private String shape;
}
Here's the PlantUML code if interested
#startuml
hide circle
entity BillingTarget {
* billingTargetID <<generated>>
--
* targetType
* targetID <<unique>>
}
entity Client {
* clientID <<fk>>
--
* name
color
shape
}
BillingTarget ||--o| Client : "targetID:clientID"
#enduml
I was already thinking of using a MappedSuperclass but right now it is only one type (though it could be more). Second the billing target may be zero and not null as there's a NOT NULL constraint already present.
I'm testing the underlying model of a HSQL database using Hibernate/Spring Boot and I've run into an issue I cannot find a solution to.
This is my simple test, I'm trying to create a shoebox entity and save it to the database with a User object set as the FK for Owner:
#TestConfiguration
static class ShoeboxServiceTestContextConfiguration {
#Bean
public ShoeboxService shoeboxService() {
return new ShoeboxService();
}
#Bean
public UserService userService() {
return new UserService();
}
}
#Autowired
UserService users;
#Autowired
ShoeboxService shoeboxes;
#Test
public void testSave()
{
System.out.println("save");
int userId = 1;
User user = new User(userId, "Foo", "hello#world.com");
user = users.save(user);
Shoebox sb = new Shoebox(user, "Name", "Context", "Comment", false);
UUID sbId = shoeboxes.save(sb).getId();
sb = shoeboxes.findOne(sbId);
assertNotNull(sb);
assertEquals(sb.getName(), "Name");
assertEquals(sb.getContext(), "Context");
assertEquals(sb.getComment(), "Comment");
assertEquals(sb.isShare(), false);
shoeboxes.deleteById(sbId);
users.deleteById(userId);
}
However when it gets it throws a
integrity constraint violation: unique constraint or index violation; SYS_PK_10126 table: USER
exception when it tries to save the Shoebox to the DB. It successfully persist the User, and it succeeds in persisting the Shoebox object when there is no Owner FK attached to it, but crashes when the FK is supplied.
Here is my User POJO:
#Entity
#Table(name="User")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class User implements Serializable
{
#Id
#Column(name = "ID")
private long ID;
#Column(name = "Name")
private String name;
#Column(name = "Email")
private String email;
#OneToOne(fetch = FetchType.LAZY)
private Shoebox currentlySelectedBox;
#OneToMany(fetch = FetchType.LAZY)
#JsonManagedReference(value="shoebox_owner")
private List<Shoebox> shoeboxes;
// Contructors, Getters/Setters etc.
}
And my Shoebox POJO:
#Entity
#Table(name="Shoebox")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Shoebox implements Serializable
{
#Id
#Column(name="ID")
UUID ID;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="OwnerID")
#JsonBackReference(value="shoebox_owner")
User owner;
#Column(name="Name")
String name;
#Column(name="Context")
String context;
#Column(name="Comment")
String comment;
#Column(name="Shared")
boolean share;
#Column(name="CreationDate")
LocalDateTime creationDate;
// Contructors, Getters/Setters etc.
}
Here is the HSQL creation script for the DB:
CREATE MEMORY TABLE PUBLIC.SHOEBOX(ID BINARY(255) NOT NULL PRIMARY KEY,COMMENT VARCHAR(255),CONTEXT VARCHAR(255),CREATIONDATE TIMESTAMP,NAME VARCHAR(255),SHARED BOOLEAN,OWNERID BIGINT)
CREATE MEMORY TABLE PUBLIC.USER(ID BIGINT NOT NULL PRIMARY KEY,EMAIL VARCHAR(255),NAME VARCHAR(255),CURRENTLYSELECTEDBOX_ID BINARY(255),CONSTRAINT FK3T924ODM2BIK5543K0E3UEGP FOREIGN KEY(CURRENTLYSELECTEDBOX_ID) REFERENCES PUBLIC.SHOEBOX(ID))
CREATE MEMORY TABLE PUBLIC.USER_SHOEBOX(USER_ID BIGINT NOT NULL,SHOEBOXES_ID BINARY(255) NOT NULL,CONSTRAINT FK5W8WMFC5E9RMEK7VC4N76MQVQ FOREIGN KEY(SHOEBOXES_ID) REFERENCES PUBLIC.SHOEBOX(ID),CONSTRAINT FKIR9SOKRCOQ33LCQTNR0LDXO93 FOREIGN KEY(USER_ID) REFERENCES PUBLIC.SHOEBOXUSER(ID),CONSTRAINT UK_508XA86IDIHP04FQD3D6GF8D7 UNIQUE(SHOEBOXES_ID))
ALTER TABLE PUBLIC.SHOEBOX ADD CONSTRAINT FK3J9RQBYW5VQ0IRF3FWYPG7LAB FOREIGN KEY(OWNERID) REFERENCES PUBLIC.USER(ID)
Why is the exception being triggered? Is there something wrong with my annotations and PK/FK relationships between the objects?
Many Thanks.
The issue is
#ManyToOne(cascade = CascadeType.ALL)
With CascadeType.ALL, any operations will extend to the other entities. So in this case the save method is cascading on the shoebox's user attempting to save it again. Since you are using a static id of 1, it is causing a key constraint.
I have an embedded PK object that doesn't populate the id field after persisting and flushing to the database. The ID is an auto-increment field in the database.
Now normally, I would just try a refresh, but it throws the following error:
"Entity no longer exists in the database: entity.Customers[ customersPK=entity.CustomersPK[ id=0, classesId=36 ] ]."
public class Customers implements Serializable {
#EmbeddedId
protected CustomersPK customersPK;
...
}
#Embeddable
public class CustomersPK implements Serializable {
#Basic(optional = false)
#Column(name = "id")
private int id;
#Basic(optional = false)
#NotNull
#Column(name = "classes_id")
private int classesId;
.....
}
And here's the code that makes the call
Classes cl = em.find(Classes.class, classId);
CustomersPK custPK = new CustomersPK();
custPK.setClassesId(cl.getId());
Customers cust = new Customers(custPK);
em.persist(cust);
em.flush();
// The problem is right here where id always equals 0
int id = cust.getCustomerspk().getId();
Thanks for the help.
Why would the id not be 0, you have never set it?
If it is a generated id, you need to annotate it using #GeneratedValue, otherwise you need to set the value.