JSON doesn't get parsed correctly into database - json

So i made a new Entity for my database, everything was going great, then tried parsing JSON into DB, and then it didn't parse it correctly. As You can see in Controller i've tried printing nazwa(name) but it showed null, don't know why. I've checked if there is any kind of errors with spelling with JSON request but it is one simple property (name). Made so many Entities with all stuff but i don't know what's wrong over here.This is a POST request at http://localhost:8080/api/typzgloszenia (Using POSTMAN) :
{
"nazwa" : "test" //name
}
I am getting this :
{
"idTypuZgloszenia": 1, // id of request type
"nazwa": null //name
}
Here is Controller :
#Controller
#RequestMapping("/typzgloszenia")
public class TypZgloszeniaController {
private iTypZgloszeniaService itypZgloszeniaService;
#Autowired
public TypZgloszeniaController (iTypZgloszeniaService itypZgloszeniaService) {
this.itypZgloszeniaService = itypZgloszeniaService;
}
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<TypZgloszeniaEntity> addRequest(TypZgloszeniaEntity typZgloszeniaEntity) {
System.out.println(typZgloszeniaEntity.getNazwa()); //shows null...
TypZgloszeniaEntity addRequest = itypZgloszeniaService.addRequest(typZgloszeniaEntity);
if (addRequest !=null) {
return new ResponseEntity<TypZgloszeniaEntity>(addRequest, HttpStatus.OK);
} else {
return new ResponseEntity<TypZgloszeniaEntity>(HttpStatus.NOT_FOUND);
}
}
/*
MUCH MORE STUFF
*/
Service :
#Service
#Transactional
public class TypZgloszeniaService implements iTypZgloszeniaService {
#Autowired
private iTypZgloszeniaDAO itypZgloszeniaDAO;
#Override
public TypZgloszeniaEntity addRequest(TypZgloszeniaEntity typZgloszeniaEntity) {
return itypZgloszeniaDAO.addRequest(typZgloszeniaEntity);
}
/*
MUCH MORE STUFF
*/
DAO :
#Repository
public class TypZgloszeniaDAO implements iTypZgloszeniaDAO {
#PersistenceContext
EntityManager em;
#Override
public TypZgloszeniaEntity addRequest(TypZgloszeniaEntity typZgloszeniaEntity) {
em.persist(typZgloszeniaEntity);
return typZgloszeniaEntity;
}
/*
MUCH MORE STUFF
*/
and Entity :
package praktyki.core.entities;
import javax.persistence.*;
/**
* Created by dawid on 04.02.15.
*/
#Entity
#Table(name = "typ_zgloszenia", schema = "public", catalog = "praktykidb")
public class TypZgloszeniaEntity {
private Integer idTypuZgloszenia; // id of request type
private String nazwa; //name
/*
ATRYBUTY
*/
#Id
#GeneratedValue
#Column(name = "id_typu_zgloszenia") // id of request type
public Integer getIdTypuZgloszenia() {
return idTypuZgloszenia;
}
public void setIdTypuZgloszenia(Integer idTypuZgloszenia) {
this.idTypuZgloszenia = idTypuZgloszenia;
}
#Basic
#Column(name = "nazwa") //name
public String getNazwa() {
return nazwa;
}
public void setNazwa(String nazwa) {
this.nazwa = nazwa;
}
/*
EQUALS I HASHCODE
*/
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TypZgloszeniaEntity)) return false;
TypZgloszeniaEntity that = (TypZgloszeniaEntity) o;
if (idTypuZgloszenia != null ? !idTypuZgloszenia.equals(that.idTypuZgloszenia) : that.idTypuZgloszenia != null)
return false;
if (nazwa != null ? !nazwa.equals(that.nazwa) : that.nazwa != null) return false;
return true;
}
#Override
public int hashCode() {
int result = idTypuZgloszenia != null ? idTypuZgloszenia.hashCode() : 0;
result = 31 * result + (nazwa != null ? nazwa.hashCode() : 0);
return result;
}
}
Log :
null
Hibernate: select next_hi from hibernate_unique_key for update
Hibernate: update hibernate_unique_key set next_hi = ? where next_hi = ?
Hibernate: insert into praktykidb.public.typ_zgloszenia (nazwa, id_typu_zgloszenia) values (?, ?)

Try to use #RequestBody annotation in controller. Like this:
public ResponseEntity<TypZgloszeniaEntity> addRequest(#RequestBody TypZgloszeniaEntity typZgloszeniaEntity) {
For more information see Spring docs. #RequestBody and #ResponseBody are well described in this thread.

Related

How to query a many to many relationship in spring boot repository

I am trying to have the api return a list of notes, associated by a many to many relationship with labels, given a label id. Spring boot automatically created a bridge table called notes_tables with a notes_id field and a labels_id field. Spring Boot also created a notes table and a labels table. I attempted the following:
#Query(value="select * from notes join notes_labels on note.id=notes_id join labels on labels_id=labels.id where labels_id=:lid", nativeQuery=true)
public List<Note> findNotesForLabel(#Param("lid") int labelId);
I just need to get this to work but I am specifically curious if I can get it to work with jpa method query. Any query will do as long as it works though.
EDIT:
Entities
Note.java
package com.example.maapi.models;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import java.util.List;
import java.util.Objects;
#Entity
#Table(name = "notes")
public class Note {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String note;
private String title;
private String status = "private";
#ManyToOne
#JsonIgnore
private User user;
#ManyToOne
#JsonIgnore
private Folder folder;
#ManyToMany
#JsonIgnore
private List<Label> labels;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Folder getFolder() {
return folder;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public void setFolder(Folder folder) {
this.folder = folder;
}
public List<Label> getLabels() {
return labels;
}
public void setLabels(List<Label> labels) {
this.labels = labels;
}
#Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Note)) {
return false;
}
Note note = (Note) o;
return id == note.id && Objects.equals(note, note.note) &&
Objects.equals(title, note.title) && Objects.equals(status,
note.status) && Objects.equals(user, note.user) &&
Objects.equals(folder, note.folder) && Objects.equals(labels,
note.labels);
}
#Override
public int hashCode() {
return Objects.hash(id, note, title, status, user, folder,
labels);
}
}
Label.java
package com.example.maapi.models;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import java.util.List;
import java.util.Objects;
#Entity
#Table(name = "labels")
public class Label {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String title;
private String status = "private";
#ManyToOne
#JsonIgnore
private User user;
#ManyToOne
#JsonIgnore
private Folder folder;
#ManyToMany(mappedBy = "labels")
#JsonIgnore
private List<Note> notes;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Folder getFolder() {
return folder;
}
public void setFolder(Folder folder) {
this.folder = folder;
}
public List<Note> getNotes() {
return notes;
}
public void setNotes(List<Note> notes) {
this.notes = notes;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Label)) {
return false;
}
Label label = (Label) o;
return id == label.id && Objects.equals(title, label.title) &&
Objects.equals(status, label.status) && Objects.equals(user,
label.user) && Objects.equals(folder, label.folder) &&
Objects.equals(notes, label.notes);
}
#Override
public int hashCode() {
return Objects.hash(id, title, status, user, folder, notes);
}
}
Services:
NoteService.java
package com.example.maapi.services;
import com.example.maapi.models.Folder;
import com.example.maapi.models.Note;
import com.example.maapi.models.User;
import com.example.maapi.repositories.FolderRepo;
import com.example.maapi.repositories.NoteRepo;
import com.example.maapi.repositories.UserRepo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
#Service
public class NoteService {
#Autowired
NoteRepo noteRepo;
#Autowired
UserRepo userRepo;
#Autowired
FolderRepo folderRepo;
public List<Note> findAllNotes(){
return noteRepo.findAllNotes();
}
public Note findNoteById(int noteId){
return noteRepo.findNoteById(noteId);
}
public List<Note> findNotesByUser(int userId){
return noteRepo.findNotesByUser(userId);
}
public Note createNoteForUser(int userId, Note note){
User user = userRepo.findUserById(userId);
note.setUser(user);
return noteRepo.save(note);
}
public List<Note> findNotesByFolder(int folderId){
return noteRepo.findNotesByFolder(folderId);
}
public Note createNoteForFolder(int folderId, Note note){
Folder folder = folderRepo.findFolderById(folderId);
note.setFolder(folder);
note.setUser(folder.getUser());
return noteRepo.save(note);
}
public int updateNote(int noteId, Note updatedNote){
Note note = noteRepo.findNoteById(noteId);
updatedNote.setUser(note.getUser());
updatedNote.setFolder(note.getFolder());
noteRepo.save(updatedNote);
if(updatedNote.equals(note)){
return 1;
} else {
return 0;
}
}
public int deleteNote(int noteId){
noteRepo.deleteById(noteId);
if(noteRepo.findNoteById(noteId) == null) {
return 1;
} else {
return 0;
}
}
// SEARCH IMPLEMENTATION
public List<Note> searchForNote(String note){
return noteRepo.searchForNote(note);
}
}
LabelService.java
So this is the spring-booty way to do this that I was able to figure out. CrudRepository has findById(Integer id) which returns an Optional object.
All you have to do is optional.get() to return the encapsulated object and then you can return the desired field (in my case List notes) with a getter.
// CrudRepo interface provides the findById method which returns an Optional<Label>
// object that may or may not exist. Optional.get() returns the encapsulated object.
public List<Note> findNotesByLabelId(int labelId) {
Optional<Label> label = labelRepo.findById(labelId);
return label.get().getNotes();
}
Try this one!
SELECT * FROM notes n INNER JOIN notes_labels nl ON nl.notes_id = n.note_id WHERE nl.labels_id = ?1
Edit:
#Entity
#Table(name = "notes")
#NamedNativeQuery(name = "Note.getNoteByLabel", resultSetMapping = "getNote",
query = "SELECT n.id,n.note,n.title,n.status FROM notes n INNER JOIN notes_labels nl ON nl.notes_id = n.note_id WHERE nl.labels_id = ?1")
#SqlResultSetMapping(name = "getNote", classes = #ConstructorResult(targetClass = Note.class,
columns = {#ColumnResult(name = "id", type = Integer.class),#ColumnResult(name = "note", type = String.class)
#ColumnResult(name = "title", type = String.class),#ColumnResult(name = "status", type = String.class)}))
public class Note {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String note;
private String title;
private String status = "private";
NoteRepo.java
#Query(nativeQuery = true)
List<Note> getNoteByLabel(int labelId);
Build a proper constructor and try this one.
You have to think on it as if it was simple POO. For example, you can use:
#Query("FROM Note n WHERE (SELECT l FROM Label l WHERE l.id = :lid) MEMBER OF labels")
public List<Note> findNotesByLabel(#Param("lid") int id);
which basically means,
get all notes where given id's label is part of the labels attribute
I don't fully know each implementation yet, surely the documentation would give a better approach, but I just came up with that problem and it did the trick

Parse string as json array from Postgre

I have a table in PostgreSQL with 2 columns - Id and coord.
Column "coord" - geo coordinates stored as a string in JSON format.
Example:
[{"lat":49.09693425316379,"lng":33.61747393628419},{"lat":49.11835977646441,"lng":33.638456496907},{"lat":49.12103137811804,"lng":33.63866144845382},{"lat":49.09694682809236,"lng":33.61746879914138},{"lat":49.08920750204137,"lng":33.61734796797724},{"lat":49.07643862058337,"lng":33.61246117651179}]
How to send such string as JSON Array of objects(POST request).
Entity without getters and setters
public class Lepcoord implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 30)
#Column(name = "tplnr")
private String tplnr;
#Size(max = 2147483647)
#Column(name = "coord")
private String coord;
Controller
#POST
#RequestMapping(value= "/lep/{voltage}", method = RequestMethod.POST, headers = "Accept=application/json")
#ResponseBody
public ResponseEntity<List<Lepcoord>> lep (#PathVariable String voltage)
{
return new ResponseEntity<>(gisDaoService.lep(voltage), HttpStatus.OK);
}
And service
#Transactional(readOnly = true)
public List <Lepcoord> lep (String voltage) {
Query query = this.em.createQuery(
" From Lepcoord ");
List <Lepcoord> rez = null;
try {
rez = (List<Lepcoord>) query.getResultList();
} catch (PersistenceException r) {
return null;
}
return rez;
}
Hibernate cant handle json type If i storeing coord as json in Postgre. May be someone knows easier way. Not to write own classes to work with Postgres json type
You are using Hibernate so it is good to use a custom UserType which knows how to handle json.
create a hibernate usertype
public class GeoJsonType implements UserType
{
protected static final int[] SQL_TYPES = { java.sql.Types.VARCHAR };
#Override
public int[] sqlTypes()
{
return SQL_TYPES;
}
#Override
public Class returnedClass()
{
return GeoEntity.class;
}
#Override
public boolean equals(Object x, Object y) throws HibernateException
{
if (x == y)
{
return true;
}
else if (x == null || y == null)
{
return false;
}
else
{
return x.equals(y);
}
}
#Override
public int hashCode(Object x) throws HibernateException
{
return x.hashCode();
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException
{
// if (rs.wasNull())
// {
// return null;
// }
//this is your json stored in db
String rsArr = rs.getString(names[0]);
if (rsArr == null)
return null;
GeoEntity detailAttr = JSON.toObject(rsArr, GeoEntity.class, null);
return detailAttr;
}
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException
{
if (value == null)
{
st.setNull(index, SQL_TYPES[0]);
}
else
{
//when stroing object into db convert it to json
GeoEntity castObject = (GeoEntity) value;
String json = JSON.toJson(castObject);
st.setString(index, json);
}
}
#Override
public Object deepCopy(Object value) throws HibernateException
{
return value;
}
#Override
public boolean isMutable()
{
return true;
}
#Override
public Serializable disassemble(Object value) throws HibernateException
{
return null;
}
#Override
public Object assemble(Serializable cached, Object owner) throws HibernateException
{
return null;
}
#Override
public Object replace(Object original, Object target, Object owner) throws HibernateException
{
return original;
}
}
Your Entity.java
#Type(type = "FQN to your GeoJsonType")
#Column(name = "geo")
public GeoEntity getGeo()
{
return geo;
}
Postgres supports the json_to_array function that should be of help here. Take a look at the documentation here.
Alternatively, there is this answer on SO: How to turn a json array into rows in postgres that could point you in the right direction.

Spring Data Rest - Exposing ID

I am trying to expose the Id of my domain on the Json response using Spring Data Rest, besides getting it on the self object. I try what I saw on the internet but it is not working. I am using Spring Boot and this is my starting class and my config class for exposing the Id.
package com.desingfreed;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
#ComponentScan("com.designfreed")
public class GaliasBackendApplication {
public static void main(String[] args) {
SpringApplication.run(GaliasBackendApplication.class, args);
}
}
package com.desingfreed.config;
import com.desingfreed.domain.Articulo;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;
import org.springframework.stereotype.Component;
#Component
public class ConfigurationRest extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.exposeIdsFor(Articulo.class);
}
}
package com.desingfreed.repositories;
import com.desingfreed.domain.Articulo;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
#RepositoryRestResource(collectionResourceRel = "articulos", path = "articulos")
public interface ArticuloRepository extends CrudRepository<Articulo, Long> {
}
package com.desingfreed.domain;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#Entity
#Table(name = "STA11")
public class Articulo {
#Id
#GeneratedValue
#Column(name = "ID_STA11")
private Long id;
#Column(name = "COD_ARTICU")
private String codigo;
#Column(name = "DESCRIPCIO")
private String descripcion;
// #OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "articulo")
// private List<Precio> precios = new ArrayList<>();
public Articulo() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCodigo() {
return codigo;
}
public void setCodigo(String codigo) {
this.codigo = codigo;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
// public List<Precio> getPrecios() {
// return precios;
// }
//
// public void setPrecios(List<Precio> precios) {
// this.precios = precios;
// }
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Articulo articulo = (Articulo) o;
if (id != null ? !id.equals(articulo.id) : articulo.id != null) return false;
if (codigo != null ? !codigo.equals(articulo.codigo) : articulo.codigo != null) return false;
return descripcion != null ? descripcion.equals(articulo.descripcion) : articulo.descripcion == null;
}
#Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (codigo != null ? codigo.hashCode() : 0);
result = 31 * result + (descripcion != null ? descripcion.hashCode() : 0);
return result;
}
#Override
public String toString() {
return "Articulo{" +
"id=" + id +
", codigo='" + codigo + '\'' +
", descripcion='" + descripcion + '\'' +
'}';
}
}
Although doing this I still can get the Id on the Json response, I still get like this:
"_embedded" : {
"articulos" : [ {
"codigo" : "111012082",
"descripcion" : "VIRGEN LEV. FRESCA X 500G",
"_links" : {
"self" : {
"href" : "http://localhost:8080/articulos/1"
},
"articulo" : {
"href" : "http://localhost:8080/articulos/1"
}
}
Thanks very much!
For your information, i've created a simple component to dynamically expose every id.
#Component
public class EntityExposingIdConfiguration extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
try {
Field exposeIdsFor = RepositoryRestConfiguration.class.getDeclaredField("exposeIdsFor");
exposeIdsFor.setAccessible(true);
ReflectionUtils.setField(exposeIdsFor, config, new ListAlwaysContains());
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
class ListAlwaysContains extends ArrayList {
#Override
public boolean contains(Object o) {
return true;
}
}
}

Entity Framework type case generic in predicate

I am working on updating to a more manageable repository pattern in my MVC 4 project that uses Entity Framework code first. I've integrated a generic base repository class that will do basic CRUD operations so I don't have to implement these in each repository I create. I have ran into an issue where my All method needs to filter there query by a deleted flag if the entity is a type of TrackableEntity. Since the Entity is generic in the base repository I am attempting to cast is to a type of TrackableEntity in the where which just results in the following error message.
The 'TypeAs' expression with an input of type 'NameSpace.Models.ClientFormField' and a check of type 'NameSpace.Models.TrackableEntity' is not supported. Only entity types and complex types are supported in LINQ to Entities queries.
This error makes complete since and I understand why the code I have is not working but I am trying to find a way to filter out deleted items without having to override this method in all of my repositories. The code I have for my All method is below.
public virtual IEnumerable<T> All()
{
if (typeof(T).IsSubclassOf(typeof(TrackableEntity)))
return dbSet.Where(e => !(e as TrackableEntity).IsDeleted).ToList();
return dbSet.ToList();
}
I know that I can do the following
public virtual IEnumerable<T> All(Expression<Func<T, bool>> predicate = null)
{
if (predicate != null)
return dbSet.Where(predicate).IsDeleted).ToList();
return dbSet.ToList();
}
And then add this to all of my repositories
public override IEnumerable<CaseType> All(Expression<Func<CaseType,bool>> predicate = null)
{
if (predicate == null)
predicate = e => !e.IsDeleted;
return base.All(predicate);
}
The problem I have with this is that I am duplicating code, this is basically a copy and paste into all of my repositories which defeats the purpose of changing to this new repository pattern. I made the switch to end duplicated code in my repositories.
Here is an example of one of my entities.
public class CaseType : TrackableEntity, IValidatableObject
{
public int Id { get; set; }
public string Name { get; set; }
public bool InUse { get; set; }
public bool IsValid { get { return !this.Validate(null).Any(); } }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (String.IsNullOrEmpty(Name))
yield return new ValidationResult("Case Type name cannot be blank", new[] { "Name" });
//Finish Validation Rules
}
}
And the TrackableEntity
public abstract class TrackableEntity
{
public bool Active { get; set; }
public bool IsDeleted { get; set; }
public virtual User CreatedBy { get; set; }
public virtual User ModifiedBy { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
}
Any help on this would be much appreciated.
I finally got a solution working that I am happy with. I ended up making 2 generic repositories. One that is the base repository which deals with all of the calls to the database for my BaseEntity which all entities inherit from. Then I made my 2nd generic repo which is inherits BaesEntity and overrides a few methods to handle the needs of my TrackableEntities. In the end this does what I want by handling the filtering of soft deleted items from within the repo and also gives me more flexibility with the TrackableEntity.
BaseRepository -
public class BaseRepository<T> : IBaseRepository<T> where T : BaseEntity
{
private readonly IAppDb _db;
private readonly IDbSet<T> _dbSet;
public BaseRepository(IAppDb db)
{
_db = db;
_dbSet = Lwdb.Set<T>();
}
protected IAppDb Lwdb
{
get { return _db; }
}
#region IBaseRepository<T> Members
public virtual T GetById(int id)
{
return _dbSet.Find(id);
}
public virtual T Add(T entity)
{
_dbSet.Add(entity);
_db.Commit();
return entity;
}
public virtual bool Any(Expression<Func<T, bool>> expression)
{
return _dbSet.Any(expression);
}
public virtual void Delete(T entity)
{
_dbSet.Remove(entity);
_db.Commit();
}
public virtual IEnumerable<T> All()
{
return _dbSet.ToList();
}
public virtual T Update(T entity, bool attachOnly = false)
{
_dbSet.Attach(entity);
_db.SetModified(entity);
if (!attachOnly) _db.Commit();
return entity;
}
#endregion
protected User GetCurrentUser()
{
return
_db.Set<User>().Find(HttpContext.Current != null ? ((User) HttpContext.Current.Session["User"]).Id : 1);
}
BaseTrackableEntityRepository -
public class BaseTrackableEntityRepository<T> : BaseRepository<T>, IBaseTrackableEntityRepository<T>
where T : TrackableEntity
{
private readonly IAppDb _db;
private readonly IDbSet<T> _teDB;
public BaseTrackableEntityRepository(IAppDb db)
: base(db)
{
_db = db;
_teDB = _db.Set<T>();
}
#region IBaseTrackableEntityRepository<T> Members
public virtual T SetDeleteFlag(int id)
{
var entity = _teDB.Find(id);
if (entity == null) return null; //throw exception
entity.IsDeleted = true;
entity.DateModified = DateTime.Now;
entity.ModifiedBy = GetCurrentUser();
return Update(entity);
}
public override IEnumerable<T> All()
{
return _teDB.Where(e => !e.IsDeleted).ToList();
}
public override T Add(T entity)
{
var curUser = GetCurrentUser();
entity.CreatedBy = curUser;
entity.ModifiedBy = curUser;
entity.DateCreated = DateTime.Now;
entity.DateModified = DateTime.Now;
entity.Active = true;
entity.IsDeleted = false;
_teDB.Add(entity);
_db.Commit();
return entity;
}
public override T Update(T entity, bool attachOnly = false)
{
InsertTeData(ref entity);
entity.ModifiedBy = GetCurrentUser();
entity.DateModified = DateTime.Now;
_teDB.Attach(entity);
_db.SetModified(entity);
if (!attachOnly) _db.Commit();
return entity;
}
public virtual T SetStatus(int id, bool status)
{
var entity = _teDB.Find(id);
if (entity == null) return null;
entity.Active = status;
return Update(entity);
}
#endregion
private void InsertTeData(ref T entity)
{
if (entity == null || entity == null) return;
var dbEntity = GetById(entity.Id);
if (dbEntity == null) return;
_db.Detach(dbEntity);
entity.CreatedBy = dbEntity.CreatedBy;
entity.DateCreated = dbEntity.DateCreated;
entity.ModifiedBy = dbEntity.ModifiedBy;
entity.DateModified = dbEntity.DateModified;
}

Hibernate n:m extractHashCode throws NullPointerException

I get the following exception while inserting an object with hibernate. Reading from the database works like a charm. I use MySQL 5.5 as database provider and hibernate 3.6.5.
I have the following database schema:
cell(id,cellid,lac,mcc,mnc,insertTime)
location(id,latitude,longitude,altitude,accuracy,heading,hdop,vdop,pdop,insertTime)
cellatlocation(servingCell,neighbourCell,location,signalStrength,insertTime)
where id in cell and location are primary keys and servingCell,neighbourCell and location is the composite primary key in cellatlocation.
java.lang.NullPointerException
at org.hibernate.type.descriptor.java.AbstractTypeDescriptor.extractHashCode(AbstractTypeDescriptor.java:88)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:196)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:191)
at org.hibernate.type.EntityType.getHashCode(EntityType.java:325)
at org.hibernate.type.ComponentType.getHashCode(ComponentType.java:222)
at org.hibernate.engine.EntityKey.generateHashCode(EntityKey.java:126)
at org.hibernate.engine.EntityKey.<init>(EntityKey.java:70)
at org.hibernate.engine.StatefulPersistenceContext.getDatabaseSnapshot(StatefulPersistenceContext.java:286)
at org.hibernate.engine.ForeignKeys.isTransient(ForeignKeys.java:211)
at org.hibernate.event.def.AbstractSaveEventListener.getEntityState(AbstractSaveEventListener.java:531)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:103)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:685)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:677)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:673)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:345)
at $Proxy17.saveOrUpdate(Unknown Source)
The classes I want to insert:
Cell.java
#Entity
#Table(name = "cell", catalog = "crisis")
public class Cell implements Serializable {
private static final long serialVersionUID = -8532796958180260393L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Integer mnc;
private Integer mcc;
private Long cellid;
private Integer lac;
#org.hibernate.annotations.Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
private DateTime insertTime;
#OneToMany(mappedBy = "pk.servingCell")
private List<CellAtLocation> cellAtLocation = new LinkedList<CellAtLocation>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getMnc() {
return mnc;
}
public void setMnc(Integer mnc) {
this.mnc = mnc;
}
public Integer getMcc() {
return mcc;
}
public void setMcc(Integer mcc) {
this.mcc = mcc;
}
public Long getCellid() {
return cellid;
}
public void setCellid(Long cellid) {
this.cellid = cellid;
}
public Integer getLac() {
return lac;
}
public void setLac(Integer lac) {
this.lac = lac;
}
public DateTime getInsertTime() {
return insertTime;
}
public void setInsertTime(DateTime insertTime) {
this.insertTime = insertTime;
}
public List<CellAtLocation> getCellAtLocation() {
return cellAtLocation;
}
public void setCellAtLocation(List<CellAtLocation> cellAtLocation) {
this.cellAtLocation = cellAtLocation;
}
}
Location.java
#Entity
#Table(name = "location", catalog = "crisis")
public class Location implements Serializable {
private static final long serialVersionUID = 2197290868029835453L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Double latitude;
private Double longitude;
private Double altitude;
private Double accuracy;
private Double heading;
private Double hdop;
private Double vdop;
private Double pdop;
#org.hibernate.annotations.Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
private DateTime insertTime;
#OneToMany(mappedBy = "pk.location")
private List<CellAtLocation> cellAtLocation = new LinkedList<CellAtLocation>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Double getLatitude() {
return latitude;
}
public void setLatitude(Double latitude) {
this.latitude = latitude;
}
public Double getLongitude() {
return longitude;
}
public void setLongitude(Double longitude) {
this.longitude = longitude;
}
public Double getAltitude() {
return altitude;
}
public void setAltitude(Double altitude) {
this.altitude = altitude;
}
public Double getAccuracy() {
return accuracy;
}
public void setAccuracy(Double accuracy) {
this.accuracy = accuracy;
}
public Double getHeading() {
return heading;
}
public void setHeading(Double heading) {
this.heading = heading;
}
public Double getHdop() {
return hdop;
}
public void setHdop(Double hdop) {
this.hdop = hdop;
}
public Double getVdop() {
return vdop;
}
public void setVdop(Double vdop) {
this.vdop = vdop;
}
public Double getPdop() {
return pdop;
}
public void setPdop(Double pdop) {
this.pdop = pdop;
}
public DateTime getInsertTime() {
return insertTime;
}
public void setInsertTime(DateTime insertTime) {
this.insertTime = insertTime;
}
public List<CellAtLocation> getCellAtLocation() {
return cellAtLocation;
}
public void setCellAtLocation(List<CellAtLocation> cellAtLocation) {
this.cellAtLocation = cellAtLocation;
}
}
CellAtLocation.java
#Entity
#Table(name = "cellatlocation", catalog = "crisis")
#AssociationOverrides({ #AssociationOverride(name = "pk.servingCell", joinColumns = #JoinColumn(name = "servingCell")),
#AssociationOverride(name = "pk.neighbourCell", joinColumns = #JoinColumn(name = "neighbourCell")),
#AssociationOverride(name = "pk.location", joinColumns = #JoinColumn(name = "location")) })
public class CellAtLocation implements Serializable {
private static final long serialVersionUID = -4440795783726362367L;
private CellAtLocationPk pk = new CellAtLocationPk();
private Integer signalStrength;
#EmbeddedId
private CellAtLocationPk getPk() {
return pk;
}
#SuppressWarnings("unused")
private void setPk(CellAtLocationPk pk) {
this.pk = pk;
}
#Transient
public Cell getServingCell() {
return getPk().getServingCell();
}
public void setServingCell(Cell cell) {
getPk().setServingCell(cell);
}
#Transient
public Cell getNeighbourCell() {
return getPk().getNeighbourCell();
}
public void setNeighbourCell(Cell cell) {
getPk().setNeighbourCell(cell);
}
#Transient
public Location getLocation() {
return getPk().getLocation();
}
public void setLocation(Location location) {
getPk().setLocation(location);
}
public Integer getSignalStrength() {
return signalStrength;
}
public void setSignalStrength(Integer signalStrength) {
this.signalStrength = signalStrength;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
CellAtLocation that = (CellAtLocation) o;
if (getPk() != null ? !getPk().equals(that.getPk()) : that.getPk() != null)
return false;
return true;
}
public int hashCode() {
return (getPk() != null ? getPk().hashCode() : 0);
}
}
and finally the primary key mapping itself CellAtLocationPk.java
#Embeddable
public class CellAtLocationPk implements Serializable {
private static final long serialVersionUID = 5286485161491158083L;
private Cell servingCell;
private Cell neighbourCell;
private Location location;
#ManyToOne
public Cell getServingCell() {
return servingCell;
}
public void setServingCell(Cell servingCell) {
this.servingCell = servingCell;
}
#ManyToOne
public Cell getNeighbourCell() {
return neighbourCell;
}
public void setNeighbourCell(Cell neighbourCell) {
this.neighbourCell = neighbourCell;
}
#ManyToOne
public Location getLocation() {
return location;
}
public void setLocation(Location location) {
this.location = location;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
CellAtLocationPk that = (CellAtLocationPk) o;
if (servingCell != null ? !servingCell.equals(that.servingCell) : that.servingCell != null)
return false;
if (neighbourCell != null ? !neighbourCell.equals(that.neighbourCell) : that.neighbourCell != null)
return false;
if (location != null ? !location.equals(that.location) : that.location != null)
return false;
return true;
}
public int hashCode() {
int result;
result = (servingCell != null ? servingCell.hashCode() : 0);
result = 31 * result + (neighbourCell != null ? neighbourCell.hashCode() : 0);
result = 31 * result + (location != null ? location.hashCode() : 0);
return result;
}
}
The problem is that hibernate is trying to save the relationship object, CellAtLocation instance, while the children objects, Cell and/or Location instances are not yet persisted. Thus, children objects don't have generated ids associated with them and therefore hibernate can not compute the hash for them.
Before trying to save CellAtLocation instance, try saving the children objects first by calling saveOrUpdate method on them.
For anyone also dealing with this issue, it occurred in my case simply because I did not have an open and active transaction. The stack trace did not point directly to this being the issue but can be explained as follows:
The parent item was being persisted in the cache and hibernate simply accepted the parent not having an actual ID. If we could have somehow called flush() on our connection we would have then been notified of the non-existent transaction. Instead, when the child item was to be persisted the parent's ID did not TRULY exist. When hibernate went to get the parent's hashed id for the purpose of saving the child, the NPE was thrown.
I had the same problem and figured out that the way to properly map embedded ids is by using #Embeddable, #EmbeddedId and #MapsId (which is the one missing in the problem code). The docs from #MapsId annotation states an example that fixes this issue:
Example:
// parent entity has simple primary key
#Entity
public class Employee {
#Id
private long employeeId;
private String name;
...
}
// dependent entity uses EmbeddedId for composite key
#Embeddable
public class DependentId {
private String name;
private long employeeId; // corresponds to primary key type of Employee
}
#Entity
public class Dependent {
#EmbeddedId
private DependentId dependentId;
...
#MapsId("employeeId") // maps the employeeId attribute of embedded id
#ManyToOne
private Employee employee;
}
This is the proper way to fix the issue. This way, you wouldn't need to save the entities separately (which is not a good practice). Instead, hibernate will manage the entire transaction for you by mapping the generated ids properly.
Hope this helps for anyone having this issue in the future.
Cheers,
In my case, found out that one primary key in a foreign key table has not set. Only the fields that implement hashcode int the table were set.
I too found the problem to be that hibernate is trying to save the relationship/parent object, while the child object instances are not yet persisted. I solved it by setting child object Ids to 0 and hibernate picked up from there without having to save the child objects manually.
Hope this helps.