I had the following entity mapped using JPA 2:
#Entity
public class Translation {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String locale;
#Column(name = "business_code",insertable = true,updatable = false,length = 200,nullable = false)
private String code;
private String text;
// Gettets and setters
....
}
Then I realized than the pair (locale,code) should be unique, so I have changed the entity to have an embeddedId composed by locale, code and I removed the column id from the mapping. This way this pair would act as primary key and they could not be repeated:
As a result:
#Entity
public class Translation {
#EmbeddedId
private TranslationId translationId;
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public TranslationId getTranslationId() {
return translationId;
}
public void setTranslationId(TranslationId translationId) {
this.translationId = translationId;
}
#Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
#Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}
And the embeddedId class:
#Embeddable
public class TranslationId implements Serializable{
private static final long serialVersionUID = 1L;
private String locale;
#Column(name = "business_code",insertable = true,updatable = false,length = 200,nullable = false)
private String code;
#Override
public boolean equals(Object obj){
return EqualsBuilder.reflectionEquals(this, obj);
}
#Override
public int hashCode(){
return HashCodeBuilder.reflectionHashCode(this);
}
public String getLocale() {
return locale;
}
public void setLocale(String locale) {
this.locale = locale;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
I'm using Spring data to query the data, so I have modified also my JPA repository to take in account the new composite Id:
#Repository
public interface TranslationRepository extends JpaRepository<Translation,TranslationId> {
}
So, first of all, does anyone see anything wrong here? Should I do it in another way? As my tests are not passing anymore, if I do a simple translationRepository.findAll(), I'm not getting any result (however there is data in the db), but I'm not getting any error message...
And second - if I get this to work, and then I want Spring data to query all the translations only by locale (not by code), how can I do it? As locale and code are now part of the primary key, can I query them independently?
Since your first problem was already fixed, I'll answer the second question
I want Spring data to query all the translations only by locale (not by code), how can I do it?
locale is still accessible as a single property via translationId. In JPQL you can write
SELECT t FROM Translation t WHERE t.translationId.locale = :locale
In Spring Data repository you can either use the #Query on a custom-named method
#Query("SELECT t FROM Translation t WHERE t.translationId.locale = :locale")
public List<Translation> findByLocale(#Param("locale") String locale)
or go with the slightly longer method name, but automatically handled by Spring Data
public List<Translation> findByTranslationIdLocale(String locale)
Related
I created a table in AWS DynamoDB which I'll be using for a basic questions and answers forum I'm developing and after table creation and some successful tests where I was able to insert data I decided to add an attribute for storing date of question, which I called time_stamp but for an unknown reason for me I'm not being able to refresh table structure, I mean, data is still saved with no errors but with no time_stamp field.
I tried deleting the table and recreating several times and modifying time_stamp data type with no success so I'm lost and I hope anyone can help me. I thought the only neccesary thing to alter table structure in DynamoDB is just modifing the mapping class in Java but cannot make it to work.
My mapping class is the next:
import com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.*;
#DynamoDBTable(tableName = "community_questions")
public class CommunityQuestion {
private long question_id;
private String time_stamp;
private String user_id;
private String subject;
private String question;
#DynamoDBHashKey(attributeName = "question_id")
public long getQuestionId() { return question_id; }
public void setQuestionId(long questionId) { this.question_id = questionId; }
#DynamoDBAttribute (attributeName = "time_stamp")
public String geTimeStamp() {
return time_stamp;
}
public void setTimeStamp(String timeStamp) {
this.time_stamp = timeStamp;
}
#DynamoDBAttribute (attributeName = "user_id")
public String getUserId() {
return user_id;
}
public void setUserId(String userId) {
this.user_id = userId;
}
#DynamoDBAttribute(attributeName = "subject")
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
#DynamoDBAttribute(attributeName = "question")
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
}
All data is being saved with no errors but time_stamp won't!!
I'm using mapper.save for saving operations.
Maybe anything I should refresh in AWS console?? Can't find anything.
Please help, and many thanks in advance.
There is a typo in your code. Rename geTimeStamp to getTimeStamp
DynamoDbMapper considers only methods with exactly "get" or "is" prefixes, and then checks for annotations on those in a second step. Its not picking up your added property because of that.
/**
* Returns whether the method given is a getter method we should serialize /
* deserialize to the service. The method must begin with "get" or "is",
* have no arguments, belong to a class that declares its table, and not be
* marked ignored.
*/
private static boolean isRelevantGetter(Method m) {
(soure)
I've been using Spring Data for saving entities to the mongo DB and my code at the moment looks like this:
I have a repo class:
public interface LogRepo extends MongoRepository<Log, String> {
}
and I have an Entity Log which looks like this:
#Document(
collection = "logs"
)
public class Log {
#Id
private String id;
private String jsonMessage;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getJsonMessage() {
return jsonMessage;
}
public void setJsonMessage(String jsonMessage) {
this.jsonMessage = jsonMessage;
}
}
and this work well for me, however this approach works only for the case if I want to save Log entities to "logs" collection. However it would be very nice for me to be able to save Log entity to different collections depending on the context. I mean it would be nice to define collection name in the runtime. Is it possible somehow?
Thanks, cheers
Try to use inheritance and define appropriate collection names in such way. May give you possibility to save in different collections but you will be still not able to specify dynamically collection names and resp. their amount at runtime.
#Document(
collection = "logs"
)
public class Log {
#Id
private String id;
private String jsonMessage;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getJsonMessage() {
return jsonMessage;
}
public void setJsonMessage(String jsonMessage) {
this.jsonMessage = jsonMessage;
}
}
#Document(
collection = "log_child"
)
public class LogChild extends Log{}
With the MongoOperations save method you can choose which class to use and
based on the class it will choose the appropriate collection.
#Document(collection = "collection_#{T(com.github.your_project.DBUtils).getCollectionName()}")
public Class Collection
You can change the name in real time using a static getter
#UtilityClass
public class DBUtils {
private String collectionName;
public String getCollectionName() {
return collectionName;
}
public void setCollectionName(String collectionName) {
DBUtils.collectionName = collectionName;
}
}
As the question says, I would like to know the easiest way to perform Data Binding to regular (non JavaFX) properties of JPA entities.
I was thinking there is probably some way to use the same binding functions if you make your entity class implement some interface for change listeners or something of the sort.
Here is an example of a JPA entity with property changed listeners.
#Entity
public class Ticket {
#Id
#GeneratedValue()
private int id;
#ManyToOne()
private EntryGate entryGate;
#ManyToOne()
private ExitGate exitGate;
#Transient
private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
public Ticket() {
}
public Ticket(EntryGate owner) {
this();
this.entryGate = owner;
}
public void addListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
public void removeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
public int getId() {
return id;
}
public EntryGate getEntryGate() {
return entryGate;
}
public void setEntryGate(EntryGate entryGate) {
EntryGate oldGate = this.entryGate;
this.entryGate = entryGate;
changeSupport.firePropertyChange("entryGate", oldGate, this.entryGate);
}
public ExitGate getExitGate() {
return exitGate;
}
public void setExitGate(ExitGate exitGate) {
ExitGate oldGate = this.exitGate;
this.exitGate = exitGate;
changeSupport.firePropertyChange("exitGate", oldGate, this.exitGate);
}
}
Here is an example of a JavaFX property binding.
this.idLabel.textProperty().bind(this.ticket.idProperty().asString());
Obviously I can't replace my JPA properties with SimpleXXXProperty... because they are entities participating in object relational mapping.
Using Ebean as ORM, I have the following Model class :
#Entity
#Table(name = "update_proposition")
public class UpdateProposition extends Model {
#EmbeddedId
public UpdatePropositionKey id;
public String fieldName;
public String oldValue;
public String newValue;
#Embeddable
public class UpdatePropositionKey implements Serializable {
#ManyToOne
#JoinColumn(name = "update_request")
public UpdateRequest updateRequest;
public Date date;
#Id
public int serial;
#Override
public int hashCode() {
return super.hashCode();
}
#Override
public boolean equals(final Object obj) {
return super.equals(obj);
}
}
}
My goal is to map a table with a primary key compound by a foreign key, a date and an auto-incremented serial number.
For the instance, this Model throws a RuntimeException: Error reading annotations. How can I implement my use case ?
Once this problem solved, how to assign the date and the foreign key ?
Will a pattern like updateProposition.id.date = Calendar.getInstance().getTime() work fine ?
Thanks for your help.
I found solution to this problem. Your error was because of #ManyToOne annotation in UpdatePropositionKey class. I moved this relation to UpdateProposition class leaving only UpdateRequest.id. So now there are two mappings from UpdateProposition class to UpdateRequest class. one is througt composite key and second through #ManyToOne relation. Both mappings use the same column. Additionally #JoinColumn annotation has attributes 'updatable and 'insertable' set to false. After these all changescode looks as follows:
#Entity
#Table(name = "update_proposition")
public class UpdateProposition extends Model {
public UpdateProposition(int aSerial, Date aDate) {
id = new UpdatePropositionKey();
id.serial = aSerial;
id.date = aDate;
}
#EmbeddedId
private UpdatePropositionKey id;
#ManyToOne
#JoinColumn(name = "update_request_id", insertable = false, updatable = false)
private UpdateRequest updateRequest;
public String fieldName;
public String oldValue;
public String newValue;
public void setUpdateRequest(UpdateRequest aUpdateRequest) {
updateRequest = aUpdateRequest;
id.updateRequest_id = aUpdateRequest.id;
}
public UpdateRequest getUpdateRequest() {
return updateRequest;
}
}
#Embeddable
public class UpdatePropositionKey implements Serializable {
#Id
public int serial;
public Date date;
public int updateRequest_id;
#Override
public int hashCode() {
return super.hashCode();
}
#Override
public boolean equals(final Object obj) {
return super.equals(obj);
}
}
I am writing a webservice to maintain a database. I am trying to use JPA (EclipseLink) for the entity classes. However, the database uses natural primary keys and therefore there's potential that an update on the ID fields will fail due to foreign key constraints. Our DBA has provided a function to update the ID fields which will create a new parent record with the updated ID, update the child records to point to the new parent and delete the old parent.
If the ID fields could be updated "normally", I would have a situation like this:
#Entity
#Table(name = "PARENT")
public class Parent implements Serializable
{
private static final long serialVersionUID = 1L;
private String parent;
private String attribute;
private Set<Child> childs;
public Parent()
{
}
#Id
#Column(name = "PARENT")
public String getParent()
{
return this.parent;
}
public void setParent(String parent)
{
this.parent = parent;
}
#Column(name = "ATTRIBUTE")
public String getAttribute()
{
return this.attribute;
}
public void setAttribute(String attribute)
{
this.attribute = attribute;
}
#OneToMany(mappedBy = "parentBean")
public Set<Child> getChilds()
{
return this.childs;
}
public void setChilds(Set<Child> childs)
{
this.childs = childs;
}
}
#Entity
#Table(name = "CHILD")
public class Child implements Serializable
{
private static final long serialVersionUID = 1L;
private String child;
private String attribute;
private Parent parentBean;
public Child()
{
}
#Id
#Column(name = "CHILD")
public String getChild()
{
return this.child;
}
public void setChild(String child)
{
this.child = child;
}
#Column(name = "ATTRIBUTE")
public String getAttribute()
{
return this.attribute;
}
public void setAttribute(String attribute)
{
this.attribute = attribute;
}
#ManyToOne
#JoinColumn(name = "PARENT")
public Parent getParent()
{
return this.parent;
}
public void setParent(Parent parent)
{
this.parent = parent;
}
}
I also have a GenericServiceBean class with a method to call functions:
#Stateless
public class GenericServiceBean implements GenericService
{
#PersistenceContext(unitName = "PersistenceUnit")
EntityManager em;
public GenericServiceBean()
{
// empty
}
#Override
public <T> T create(T t)
{
em.persist(t);
return t;
}
#Override
public <T> void delete(T t)
{
t = em.merge(t);
em.remove(t);
}
#Override
public <T> T update(T t)
{
return em.merge(t);
}
#Override
public <T> T find(Class<T> type, Object id)
{
return em.find(type, id);
}
. . .
#Override
public String executeStoredFunctionWithNamedArguments(String functionName,
LinkedHashMap<String, String> namedArguments)
{
Session session = JpaHelper.getEntityManager(em).getServerSession();
StoredFunctionCall functionCall = new StoredFunctionCall();
functionCall.setProcedureName(functionName);
functionCall.setResult("RESULT", String.class);
for (String key : namedArguments.keySet())
{
functionCall.addNamedArgumentValue(key, namedArguments.get(key));
}
ValueReadQuery query = new ValueReadQuery();
query.setCall(functionCall);
String status = (String)session.executeQuery(query);
return status;
}
}
If I set the ID fields to be not editable:
#Id
#Column(name = "PARENT", udpatable=false)
public String getParent()
{
return this.parent;
}
and call parent.setParent(newParent) will this still update the ID in the entity object? How does this affect any child entities? Will they also be updated (or not)?
Another scenario I don't know how to deal with is where I need to update both the ID and another attribute. Should I call the function which updates (and commits) the ID in the database then make calls to set both the ID and attribute via the normal set* methods and then the persistence context will only commit the attribute change?
Perhaps this is a situation where JPA is not appropriate?
Any advice on this is greatly appreciated.
If I set the ID fields to be not editable (...) and call parent.setParent(newParent) will this still update the ID in the entity object? How does this affect any child entities? Will they also be updated (or not)?
updatable=false means that the column won't be part of the SQL UPDATE statement regardless of what you do at the object level so the Id shouldn't be updated. And I'm also tempted to say that child entities shouldn't be affected, especially since you're not cascading anything.
Another scenario I don't know how to deal with is where I need to update both the ID and another attribute (...)
Well, my understanding is that you'd have to call the function anyway so I would call it first.
Perhaps this is a situation where JPA is not appropriate?
I'm not sure raw SQL would deal better with your situation. Actually, the whole idea of changing primary keys sounds strange if I may.