I've done the following mapping:
#Entity
#Table(name = "NWS_NEWS")
public class News implements Serializable {
private static final long serialVersionUID = 5246618151933389186L;
private String id;
private List<Picture> pictures;
+ OTHER fields / getters / setters, no matter
#Id
#GeneratedValue(generator = "juuid")
#Column(length = 36)
public String getId() {
return id;
}
#CollectionOfElements
#JoinTable(name = "NWS_PICTURES",joinColumns = #JoinColumn(name="NEWS_ID"))
#CollectionId(
columns= #Column(name="PICTURE_ID"),
type=#Type(type="long"),
generator="sequence")
public List<Picture> getPictures() {
return pictures;
}
public void setPictures(List<Picture> pictures) {
this.pictures = pictures;
}
}
And my picture is:
#Embeddable
public class Picture implements Serializable {
private static final long serialVersionUID = -1397366206984323622L;
private News news;
private String path;
private ImageSize imageSize;
#Parent
public News getNews() {
return this.news;
}
#Column(name = "path", nullable=false)
public String getPath() {
return path;
}
#Enumerated(EnumType.STRING)
#Column(name = "size", nullable=false)
public ImageSize getImageSize() {
return imageSize;
}
public void setImageSize(ImageSize imageSize) {
this.imageSize = imageSize;
}
public void setNews(News news) {
this.news = news;
}
public void setPath(String path) {
this.path = path;
}
}
And my dao test is:
#Test
public void testAddPicturesToNews() {
News newsToSave = new News();
// Create big picture
Picture pBig = new Picture();
pBig.setImageSize(ImageSize.BIG);
pBig.setPath("/tmp/blabla_big.jpg");
// Create medium picture
Picture pMedium = new Picture();
pMedium.setImageSize(ImageSize.MEDIUM);
pMedium.setPath("/tmp/blabla_med.jpg");
// Set the pictures in the news
List<Picture> picturesList = new ArrayList<Picture>();
picturesList.add(pBig);
picturesList.add(pMedium);
newsToSave.setPictures(picturesList);
// Save the news
this.newsDAO.saveOrUpdate(newsToSave);
String newsId = newsToSave.getId();
News newsLoaded = this.newsDAO.findById(newsId);
List<Picture> picturesLoaded = newsLoaded.getPictures();
for ( Picture pictureLoaded : picturesLoaded ) {
System.out.println(pictureLoaded.getPath());
System.out.println(pictureLoaded.getImageSize());
System.out.println(pictureLoaded.getNews());
System.out.println("\n");
}
}
But the output is:
/tmp/blabla_big.jpg
BIG
null
/tmp/blabla_med.jpg
MEDIUM
null
Actually i don't understand why getNews() returns null in the child entity entity, while it has the "#Parent" annotation. Am i doing something wrong?
Anyway the concept of getting the parent in a child entity seems a bit strange for me since what would happen if i do something like that:
News news1 = new News();
News news2 = new News();
List<Picture> picList = new ArrayList<Picture>();
Picture picture1 = new Picture();
picturesList.add(picture1);
picture1.setNews(news2);
news1.setPictures(picList);
this.newsDAO.saveOrUpdate(news1);
this.newsDAO.saveOrUpdate(news2);
What would happen since the same picture will be in news1 list, but also its parent was set to news2???
I think i'll do without that parent, i don't need that so much but it's just curiosity...
Thanks
Btw i'd like to have only one picture of each size for a news -> there can't be 2 small pictures for the same news.
So is it possible to add a unique constraint on {news_id , imageSize} in my embedded entity? I don't see how to do that since no id field is declared in my Picture embeddable entity
I'm not familiar with the #Parent annotation for an #Embeddable, but for "real" relationships it's always recommended to do something like this:
// News class
public void setPictures(List<Picture> pictures) {
this.pictures = pictures;
for (Picture picture : pictures) {
picture.setNews(this);
}
}
public void addPicture(Picture picture) {
this.pictures.add(picture);
picture.setNews(this);
}
Remember that OOP, as opposed to the relational model, has only the notion of a "one-way" navigation, and that you should build the "two-way" by yourself. Encapsulating this behavior in the setter makes this transparent to your consumers. So, I'm not sure why your #Parent is not working, but I would try to do the opposite:
// what you have:
newsToSave.setPictures(picturesList);
// what I'd try:
pMedium.setNews(newsToSave);
What would happen since the same picture will be in news1 list, but also its parent was set to news2???
Well, an #Embeddable is an object which is "embedded" into another, meaning that it belongs only to that other object (parent). As such, it should contain only one parent. If you change the parent, it'll belong only to this new parent. If you need an object (Picture) to have a relationship with other objects (News), you'll need a #ManyToMany (if the other object, News, may also be linked to several Picture)
Related
I have a project that I have worked with Room 2.2.5, I just updated to version 2.3.0 this is the code of an entity called photo:
#Entity(tableName = Photo.TABLE_NAME, foreignKeys = #ForeignKey(entity = Event.class,
parentColumns = "_id",
childColumns = "id_event_ft",
onDelete = ForeignKey.CASCADE))
public class Photo {
public static final String TABLE_NAME = "Photo";
public static final String COLUMN_ID = BaseColumns._ID;
#PrimaryKey(autoGenerate = true)
#ColumnInfo(index = true, name = COLUMN_ID)
public Long id_photo;
#ColumnInfo(name = "path")
private String path;
#ForeignKey(entity = Event.class,
parentColumns = "_id",
childColumns = "id_event_ft",
onDelete = ForeignKey.CASCADE)
private Long id_event_ft;
public Photo(Long id_photo, String path, Long id_event_ft) {
this.id_photo = id_photo;
this.path = path;
this.id_event_ft = id_event_ft;
}
public Long getId_photo() {
return id_photo;
}
public void setId_photo(Long id_photo) {
this.id_photo = id_photo;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Long getId_event_ft() {
return id_event_ft;
}
public void setId_event_ft(Long id_event_ft) {
this.id_event_ft = id_event_ft;
}
}
Now I am getting the following error when trying to compile
error: annotation type not applicable to this kind of declaration
#ForeignKey(entity = Event.class, parentColumns = "_id", childColumns = "id_event_ft", onDelete = ForeignKey.CASCADE)
^
The error is in the #ForeignKey that is above the variable private Long id_event_ft;
In the documentation I found this:
Added missing target to #ForeignKey annotation preventing its usage outside of the #Entity annotation. (Iced1e)
It is clear that using #ForeignKey outside of the #Entity annotation is no longer allowed, but then how do I bind the id_event_ft variable to the foreign key?, How do I assign a value to it now?
I hope someone can help me, thank you very much
Using ForeignKey does not automatically (magically) make a relationship. Rather it allows a relationship to be supported primarily by enforcing referential integrity.
There is no need for a ForeignKey definition for a Foreign Key (relationship) to exist.
That is it is defining a rule that says that the value of the child column (id_event_ft) MUST be a value that is present in the parent column (_id). It also supports handling if there is a Foreign Key Conflict (e.g. onDelete as you have used).
Actually providing a suitable value is something that you have to do programmatically, that is id adding a photo you have to determine which Event the photo is to be linked/related to.
You can use #Relation to simplify extracting related data.
So consider the following:
An Event Entity (nice and simple for demonstration)
#Entity
public class Event {
#PrimaryKey(autoGenerate = true)
Long _id = null;
String other_columns;
public Event(){}
#Ignore
public Event(String other_columns) {
this.other_columns = other_columns;
}
}
A Photo's parent column will be the _id column.
Second Ignored (i.e. Ignored by Room) constructor otherwise Room issue a warning like *warning: There are multiple good constructors and Room will pick the no-arg constructor. *
A slightly changed Photo Entity :-
#Entity(tableName = Photo.TABLE_NAME,
foreignKeys = #ForeignKey(
entity = Event.class,
parentColumns = "_id",
childColumns = "id_event_ft",
onDelete = ForeignKey.CASCADE),
indices = #Index("id_event_ft") //<<<<<<<<<< ADDED as Room warns if omitted
)
public class Photo {
public static final String TABLE_NAME = "Photo";
public static final String COLUMN_ID = BaseColumns._ID;
#PrimaryKey(autoGenerate = true)
#ColumnInfo(index = true, name = COLUMN_ID)
public Long id_photo;
#ColumnInfo(name = "path")
private String path;
/* <<<<<<<< COMMENTED OUT >>>>>>>>>>
#ForeignKey(entity = Event.class,
parentColumns = "_id",
childColumns = "id_event_ft",
onDelete = ForeignKey.CASCADE)
*/
private Long id_event_ft;
public Photo(Long id_photo, String path, Long id_event_ft) {
this.id_photo = id_photo;
this.path = path;
this.id_event_ft = id_event_ft;
}
public Long getId_photo() {
return id_photo;
}
public void setId_photo(Long id_photo) {
this.id_photo = id_photo;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Long getId_event_ft() {
return id_event_ft;
}
public void setId_event_ft(Long id_event_ft) {
this.id_event_ft = id_event_ft;
}
}
For demonstration of retrieving via a relationship the POJO EventWithPhotos :-
public class EventWithPhotos {
#Embedded
Event event;
#Relation(entity = Photo.class,parentColumn = "_id",entityColumn = "id_event_ft")
List<Photo> photos;
}
Now a Dao AllDao:-
#Dao
interface AllDao {
#Insert
long insert(Event event);
#Insert
long insert(Photo photo);
#Transaction
#Query("SELECT * FROM event")
List<EventWithPhotos> getAllEventsWithPhotos();
}
How do I assign a value to it now?
Now an example that puts it all together adding 2 Events the first with 2 photos, the second with 1 photo. Note the different techniques used:-
dao = db.getAllDao();
// Prepare to add an Event
Event newEvent = new Event();
newEvent.other_columns = "Event1";
// Add the Event retrieving the id (_id column)
long eventId = dao.insert(newEvent);
// Prepare a photo to be added to Event1
Photo newPhoto = new Photo(null,"photo1",eventId);
// Add the Photo to Event1
long photoid = dao.insert(newPhoto);
// Add second photo to Event 1 using the 2nd constructor
dao.insert(new Photo(null,"photo2",eventId));
// Add Event2 with a photo all in a single line (again using the 2nd constrcutor)
long event2Id;
dao.insert(new Photo(null,"photo3",event2Id = dao.insert(new Event("Event2"))));
// Get and output Each Event with the Photos for that Event
List<EventWithPhotos> allEventsWithPhotosList = dao.getAllEventsWithPhotos();
for (EventWithPhotos ewp: allEventsWithPhotosList) {
Log.d("EVENTPHOTOINFO","Event is " + ewp.event.other_columns);
for (Photo p: ewp.photos) {
Log.d("EVENTWITHPHOTO","\tPhoto is " + p.getPath() + " ID is " + p.getId_photo());
}
}
Result
When run the log contains :-
D/EVENTPHOTOINFO: Event is Event1
D/EVENTWITHPHOTO: Photo is photo1 ID is 1
D/EVENTWITHPHOTO: Photo is photo2 ID is 2
D/EVENTPHOTOINFO: Event is Event2
D/EVENTWITHPHOTO: Photo is photo3 ID is 3
The Database (view with Database Inspector) shows:-
The Event table :-
The Photo table :-
Relations
#Many To One
if the relation is many photo for one event, use ORM Hibernate, this is the easiest way to define foreign key with constraint on your Photo Entity
#ManyToOne
#JoinColumn(name = "id", nullable = false)
private Event id_event_ft;
I have a table of images, each having a many to many relationship to a table of galleries. I'm trying to use a org.hibernate.Criteria to return all images that are linked a given list of galleries.
I have been trying the following, where I loop over the list of galleries and create a new createCriteria for each (e.g. one inner join per required gallery), this works with just one gallery. However when I try multiple I get a org.hibernate.QueryException: duplicate association path: galleries error...
ImagesDAOCustom.java
public Images getRandomImagesInGallerys(String[] galleryList) {
Session session = sessionFactory.getCurrentSession();
Criteria criteria = session.createCriteria(Images.class, "image");
// We want an image that is part of all galleries so we need to create
// a join per gallery.
for (String gallery: galleryList) {
criteria.createCriteria("image.galleries", gallery);
criteria.add(Restrictions.eq(gallery + ".name",gallery));
}
criteria.setProjection(Projections.distinct(Projections.property("id")));
List<Integer> imageIdList = criteria.list();
Images result;
if (imageIdList.size() > 0) {
int index = rand.nextInt(imageIdList.size());
result = imagesDAO.findOne(imageIdList.get(index));
} else {
result = null;
}
return result;
}
Images.java
public class Images {
#Id
#GeneratedValue
#Column(name="ugc_images_id")
private int id;
#JsonIgnore
#JsonView(Views.Internal.class)
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name="ugc_image_galleries", joinColumns={#JoinColumn(name="image_id")}, inverseJoinColumns={#JoinColumn(name="gallery_id")})
private Set<Galleries> galleries = new HashSet<>();
...
}
Galleries.java
public class Galleries }
#Id
#GeneratedValue
#Column(name="ugc_galleries_id")
private int id;
#JsonManagedReference
#ManyToMany(mappedBy="galleries")
private Set<Images> images = new HashSet<>();
#Column(name="name")
private String name;
...
}
I was having the same problem and i search for hours and hours.
Here is what worked for me :
public Images getRandomImagesInGallerys(String[] galleryList) {
String query = "from Images ";
boolean first = true;
for(String string : galleryList) {
if(first){
query+="where '"+string+"' member of galleries ";
first=false;
} else {
query+="or '"+string+"' member of galleries ";
}
}
entityManager.createQuery(query, Images.class).getResultList();
}
Hope it help someone.
I am fairly new to entity frameworks, and don't understand why my entity does not get saved correctly.
edit:
I will try to out this test code soon, and see if it works:
#Transactional
public void doConvert(String lakoKod) {
LsZFutelszHavi futelszHavi = new LsZFutelszHavi();
//Give it values like:
futelszHavi.setOrigKorrOssz(new BigDecimal(500));
LsTLako lako = lsTLakoRepository.findOneByLakoKod(lakoKod);
LsZFutelsz futelsz = lsZFutelszRepository.findOneByLsTLako(lako);
//The modification which not get saved into tha Firebird table in the original code. Will it be saved now?
futelsz.setSzezVegenBefOssz(new BigDecimal(100));
futelszHavi.setLsZFutelsz(futelsz);
lsZFutelszHaviRepository.save(futelszHavi);
}
I am trying to convert from an old DBASE database, to a Firebird database. The Firebird database have it's tables mapped by entities. I read a DBASE table, then convert it row by row.
I use #Transactional, to either save all converted entities of the DBASE table, or save none.
I managed to convert every table correctly into the new database except one.
In the other tables, I only had to save one entity per record, and did not have to modify other type of entities. I usually had to create a Type X entity, connect it to a Type Y entity, then save. For save, I use a repository for the entity (an org.springframework.data.repository.PagingAndSortingRepository if it matters)
In that particular DBASE table, I have to create one entity of Type A, connect it to a Type B entity, modify the Type B entity, then save. The problem is, that Type B entity modification does not get saved into the Firebird table. Again, I use a repository to save the entity.
To get the Type B entity, I use it's repository's method:
LsZFutelsz findOneByLsTLako(LsTLako lsTLako);
I guessed, that maybe if I save this Type B entity with it's own repository, it would get modified in the database correctly. It did not help.
Please tell me if additional information is needed.
I copy my slightly altered code here (removed some logging, added some comments). LsZFutelszHavi is the Type A EntityClass, LsZFutelsz is type B EntityClass.
The Konverter abstract class. It is inherited for every DBASE table
public abstract class Konverter<RepositoryType extends CrudRepository<EntityType,Integer>, EntityType> {
protected String dbfPath;
protected DBaseTable sourceTable = null;
protected Logger logger;
protected RepositoryType repository;
protected String dBaseEncoding = DBaseTable.CP852;
public Konverter(String dbfPath, Logger logger, RepositoryType repository) {
this.dbfPath = dbfPath;
this.logger = logger;
this.repository = repository;
}
/*
This method should be called, to start converting
*/
#Transactional
public void konvert() {
try {
/*It loads the DBASE database*/
File sourceFile = new File(fileName);
sourceTable = new DBaseTable(sourceFile, dBaseEncoding);
sourceTable.open(IfNonExistent.ERROR);
Iterator<Record> recordIterator = sourceTable.recordIterator();
int count = 0;
try {
/*Converts the database table row by row*/
count = konvertSorok(recordIterator);
} catch (Exception e) {
throw e;
} finally {
sourceTable.close();
}
}
catch (CorruptedTableException | IOException | RuntimeException e) {
logger.error(QsLoggerUtils.getStackTraceString(e));//e.printStackTrace();
}
}
private int konvertSorok(Iterator<Record> recordIterator) {
int count = 0;
/*Converts the database table row by row*/
while(recordIterator.hasNext())
{
Record record = recordIterator.next();
/* Converting one row */
List<EntityType> entityIterable = konvertToEntity( record );
for (EntityType entityType : entityIterable) {
repository.save(entityType);
}
count++;
}
return count;
}
/**
* This should be implemented in the child method
* #param record
* #return
*/
protected abstract List<EntityType> konvertToEntity(Record record);
}
The child Class, that implmenets konvertToEntity method.
public class Konvert14FutelszHavi extends Konverter<LsZFutelszHaviRepository,LsZFutelszHavi> {
private static Logger logger = LoggerFactory.getLogger(Konvert12Futalany.class);
LsZFutelszHaviRepository lsZFutelszHaviRepository;
LsTLakoRepository lsTLakoRepository;
LsZFutelszRepository lsZFutelszRepository;
LsTEvhoRepository lsTEvhoRepository;
#Autowired
public Konvert14FutelszHavi(LsZFutelszHaviRepository lsZFutelszHaviRepository,
LsTLakoRepository lsTLakoRepository,
LsZFutelszRepository lsZFutelszRepository,
LsTEvhoRepository lsTEvhoRepository) throws IOException {
super(DBaseTable.chkFile(AppKonvertLax.PATH_LSZBF, AppKonvertLax.SOURCE_FILE_FUTELSZ_HAVI), logger, lsZFutelszHaviRepository);
dBaseEncoding = DBaseTable.CP1250;
this.lsTLakoRepository = lsTLakoRepository;
this.lsZFutelszHaviRepository = lsZFutelszHaviRepository;
this.lsZFutelszRepository = lsZFutelszRepository;
this.lsTEvhoRepository = lsTEvhoRepository;
}
#Override
protected List<LsZFutelszHavi> konvertToEntity(Record record) {
String ukod = record.getStringValue("UKOD").substring(1).trim();
BigDecimal ekaptam = new BigDecimal(record.getNumberValue("EKAPTAM").toString());
BigDecimal efutkul = new BigDecimal(record.getNumberValue("EFUTKUL").toString());
ArrayList<LsZFutelszHavi> returnArray = new ArrayList<LsZFutelszHavi>();
LsTLako lsTLako = lsTLakoRepository.findOneByLakoKod(ukod);
LsZFutelsz lsZFutelsz = lsZFutelszRepository.findOneByLsTLako(lsTLako);
if (lsZFutelsz == null) {
return returnArray;
}
/* Here is the modification in the lsZFutelsz (Type B) entity */
lsZFutelsz.setSzezVegenBefOssz(ekaptam);
/* From 10th month to 4th */
for (int i=10; i!=5; i++) {
if (i==13) {
i = 1;
}
String keyNumber = Integer.toString(i);
if (keyNumber.length() == 1) {
keyNumber = "0" + keyNumber;
}
BigDecimal fk = new BigDecimal(record.getNumberValue("FK_"+keyNumber).toString());
LsZFutelszHavi lsZFutelszHavi = new LsZFutelszHavi();
LsTEvho lsTEvho = lsTEvhoRepository.findOneByEvAndHo(2014, i);
lsZFutelszHavi.setLsTEvho(lsTEvho);
lsZFutelszHavi.setFizKorrOssz(fk);
lsZFutelszHavi.setOrigKorrOssz(efutkul);
/* This should be enough to save the lsZFutelsz entity modification I would think */
lsZFutelszHavi.setLsZFutelsz(lsZFutelsz);
returnArray.add(lsZFutelszHavi);
}
/* Even this does not help */
lsZFutelszRepository.save(lsZFutelsz);
return returnArray;
}
}
Repository for the Type B entity
#RepositoryRestResource(collectionResourceRel = LsZFutelszHavi.VERB_FUTELSZ, path = LsZFutelszHavi.VERB_FUTELSZ)
public interface LsZFutelszRepository extends PagingAndSortingRepository<LsZFutelsz, Integer> {
/*-----------------------------------------------------------------------------------------------*/
#RestResource(exported=false)
#Modifying
#Query(value="DELETE FROM ls_z_futelsz f WHERE f.lako_id = ?1", nativeQuery=true)
void deleteByLako(Integer integer);
/*-----------------------------------------------------------------------------------------------*/
LsZFutelsz findOneByLsTLako(LsTLako lsTLako);
}
Repository for the Type A entity
#RepositoryRestResource(collectionResourceRel = LsZFutelsz.VERB_FUTELSZHAVI, path = LsZFutelsz.VERB_FUTELSZHAVI)
public interface LsZFutelszHaviRepository extends PagingAndSortingRepository<LsZFutelszHavi, Integer> {
}
Entity Type A
#Entity
#Table(name="LS_Z_FUTELSZ_HAVI")
#NamedQuery(name="LsZFutelszHavi.findAll", query="SELECT l FROM LsZFutelszHavi l")
public class LsZFutelszHavi extends Audit implements Serializable {
public static final String VERB_FUTELSZ = "futelszamolasok";
/*-----------------------------------------------------------------------------------------------*/
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="GenFutelszHaviID", sequenceName="GEN_LS_Z_FUTELSZ_HAVI_ID", allocationSize= 1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="GenFutelszHaviID")
#Column(name="HAVI_ELSZ_ID")
private Integer haviElszId;
#NotNull
#Column(name="FIZ_KORR_OSSZ")
private BigDecimal fizKorrOssz;
#NotNull
#Column(name="ORIG_KORR_OSSZ")
private BigDecimal origKorrOssz;
//uni-directional many-to-one association to LsFSzlafej
#ManyToOne
#JoinColumn(name="SZLA_ID")
private LsFSzlafej lsFSzlafej;
//uni-directional many-to-one association to LsTEvho
#ManyToOne
#JoinColumns({
#JoinColumn(name="EV", referencedColumnName="EV"),
#JoinColumn(name="HO", referencedColumnName="HO")
})
private LsTEvho lsTEvho;
//bi-directional many-to-one association to LsZFutelsz
#ManyToOne
#JoinColumn(name="ELSZ_ID")
private LsZFutelsz lsZFutelsz;
public LsZFutelszHavi() {
}
//[... setters getters ...]
}
Entity Type B
#Entity
#Table(name="LS_Z_FUTELSZ")
#NamedQuery(name="LsZFutelsz.findAll", query="SELECT l FROM LsZFutelsz l")
public class LsZFutelsz extends Audit implements Serializable {
public static final String VERB_FUTELSZHAVI = "futelszhavi";
/*-----------------------------------------------------------------------------------------------*/
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="GenFutelszID", sequenceName="GEN_LS_Z_FUTELSZ_ID", allocationSize= 1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="GenFutelszID")
#Column(name="ELSZ_ID")
private Integer elszId;
#NotNull
#Column(name="LEOLV_FOGY_GJ")
private BigDecimal leolvFogyGj = BigDecimal.ZERO;
#NotNull
#Column(name="LEOLV_FOGY_OSSZ")
private BigDecimal leolvFogyOssz = BigDecimal.ZERO;
#NotNull
#Column(name="ELOZ_SZEZ_OSSZ")
private BigDecimal elozSzezOssz = BigDecimal.ZERO;
#NotNull
#Column(name="SZEZ_VEGEN_BEF_OSSZ")
private BigDecimal szezVegenBefOssz = BigDecimal.ZERO;
#NotNull
#Column(name="SZOSZT_UTAN_FENNM")
private BigDecimal szosztUtanFennm = BigDecimal.ZERO;
#NotNull
#Column(name="SZOSZTANDO_KULONB")
private BigDecimal szosztandoKulonb = BigDecimal.ZERO;
//uni-directional many-to-one association to LsTLakok
#ManyToOne
#JoinColumn(name="LAKO_ID")
private LsTLako lsTLako;
//bi-directional many-to-one association to LsZFutelszHavi
#OneToMany(mappedBy="lsZFutelsz", cascade={CascadeType.REMOVE})
private List<LsZFutelszHavi> lsZFutelszHaviTetelek;
public LsZFutelsz() {
}
//[... setters getters ...]
}
The code works, The field is just always happen to be the default value.
After testing it with simple methods, the other entity got saved into the database as well. Then I put a breakpoint into the original code which only get breaked, if the new value for the entity is different than the default. The program converted everything without breaking at the breakpoint.
So, I looked up which database I converting, and see what the contents ar. To my surprise it was always zero, the default value.
It's awkward. I was so sure, I misunderstood something, and coded wrong.
I need to get all events connected to a user.
I created a model to handle this relation:
#Entity
public class Affiliated extends Model{
#OneToMany
private Bruker bruker;
#OneToOne
private Event event;
#Column(nullable = false)
private Calendar alarmTime = null;
public enum Status{
ATTENDING, MAYBE, NOT_ATTENDING, UNDECIDED
}
private Status status;
public Affiliated(){
this.status=Status.UNDECIDED;
}
public static Finder<Event, Affiliated> find = new Model.Finder<Event, Affiliated> (
Event.class, Affiliated.class
);
I have tried to do this:
public static Result getEvents(){
if(Bruker.signedIn()) {
List<models.Event> eventList = models.Event.find.where().eq("creator", Bruker.find.byId(session("User"))).findList();
List<Affiliated> affiliatedList = models.Affiliated.find.where().eq("bruker", Bruker.find.byId(session("User"))).findList();
for (Affiliated i : affiliatedList) {
eventList.add(i.getEvent());
}
return Bruker.signedIn(ok(views.html.layoutHtml.render("MyEvents", views.html.Event.myEvents.render(eventList))));
}
return redirect(routes.Application.index().absoluteURL(request()));
}
But it returns a nullpointerException for affiliatedList when I try to run it, even though there should be no problem returning an empty list.
Any thoughts on how to fix this error?
ps: "bruker" means "user" in norwegian.
I am using the GAE Datastore for the application
I am in despair ... I hit a mental block over here and I just cant think of anything to solve this anymore. I have a class Teacher.class (and all of its CASCADES) that wont store in its Namespace ... all other classes (not shown here, but very similar) work like a charm, the Teacher.class reads and writes and all perfectly ... except if WONT go to its Namespace, it always ends up in the Empty Namespace.
I am going to post the class, along with the data interface layer.
#Entity
public class Teacher implements Serializable
{
private static final long serialVersionUID = 5426530769458891752L;
#Id
private Key key;
private long KID;
private long school;
private String FName;
private String LName;
private String Email;
private String SchoolName;
#OneToOne(cascade = CascadeType.ALL,fetch=FetchType.LAZY)
private Transcript Transcript; // Contains further #OneToMany Relations and Constructors
#OneToOne(cascade = CascadeType.ALL,fetch=FetchType.LAZY)
private TeacherInfo teacherInf; // Contains Only Primitive Type Objects(not important)
private Boolean ActiveUser = false;
private List<Key> WorkshopsAttended;
private List<Key> WorkshopsRegistered;
public Teacher()//Constructor
{
if(this.KID == 00)
{
this.KID = TeacherUtils.genKID();//Returns a sequence and date and location based long
this.key = KeyFactory.createKey(Teacher.class.getSimpleName(), this.KID);
this.Transcript = new Transcript();
this.teacherInf = new TeacherInfo();
}
if(this.WorkshopsAttended == null)
{
this.WorkshopsAttended = new ArrayList<Key>();
}
if(this.WorkshopsRegistered == null)
{
this.WorkshopsRegistered = new ArrayList<Key>();
}
} //End of Constructor
//Getters and Setters
}
Here is the Transcript Class
#Entity
public class Transcript implements Serializable
{
private static final long serialVersionUID = -6677626465437896027L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key ID;
#OneToOne(cascade = CascadeType.ALL, mappedBy="Transcript",fetch=FetchType.LAZY)
private Teacher teacher;
public Transcript()//Constructor
{
if(this.C1== null)
{this.C1= new Course1();}
if (this.C2== null)
{this.C2= new Course2();}
if (this.C3== null)
{this.C3= new Course3();}
}//End of Constructor
#OneToOne(cascade = CascadeType.ALL,fetch=FetchType.EAGER)
private Course1 C1; // Contains only primitive feilds
#OneToOne(cascade = CascadeType.ALL,fetch=FetchType.EAGER)
private Course2 C2; // Contains only primitive feilds
#OneToOne(cascade = CascadeType.ALL,fetch=FetchType.EAGER)
private Course3 C3;
// Getters and Setters
Now Lastly the Data Interface Layer (This is a very long long file and I cant put it all in here, so I am just gonna paste the bits that have to do with persisting Teacher entities)
public class TeacherUtils
{
private static final String ActiveNamespace = SystemSettings.TeacherActive;
private static final String DeletedNamespace = SystemSettings.TeacherDeleted;
private static final boolean NSFlag = (SystemSettings.UseNameSpace & SystemSettings.TeacherNameSpace);
public synchronized static void SaveTeacher(Teacher teacher)
{
EntityManager em = getActiveEM();
em.persist(teacher);
em.flush();
closeEM(em);
}// End of SaveTeacher
public synchronized static void UpdateTeacher(Teacher teacher)
{
EntityManager em = getActiveEM();
em.merge(teacher);
em.flush();
closeEM(em);
}// End of Update Teacher
private synchronized static EntityManager getActiveEM()
{
EntityManager em;
try
{
if( (!NamespaceManager.get().equals(ActiveNamespace)) && TeacherUtils.NSFlag)
{
setNamespace();
}
}
finally
{
em = EMF.get().createEntityManager();
em.getTransaction().begin();
}
return em;
} // End of getAciveEM();
private synchronized static void closeEM(EntityManager em)
{
em.getTransaction().commit();
em.close();
NamespaceManager.set("");
while(!NamespaceManager.get().equals(""))
{}
return;
}// End of CloseEM(em)
private synchronized static void setNamespace()
{
if(TeacherUtils.NSFlag)
{
NamespaceManager.set(TeacherUtils.ActiveNamespace);
while(!NamespaceManager.get().equals(TeacherUtils.ActiveNamespace))
{}
}
}// End of setNamespace
A typical example in the Business Logic Layer would be
Teacher teacher = new Teacher();
teacher.setFName("John");
teacher.setLName("Smith");
teacher.setEmail("xyz#xyz.com");
TeacherUtils.SaveTeacher(teacher);
I would like to thank everyone who tried looking into this question ! Turns out there was something wrong with the Teacher.class constructor logic that caused a change in the Namespace, without reverting it to its previous state (the ID generator function). Now that I took care of it all works great !! I think it's just one of those days !!
Again thanks alot and I will be leaving this code as a template incase someone needs it.