This is my database:
CREATE TABLE other (
id integer primary key autoincrement not null,
texto text
);
CREATE TABLE tabela (campoid INTEGER primary key autoincrement not null,
camponome text not null, chave int,
foreign key (chave) references other(id));
and this is my classes:
#DatabaseTable(tableName = "tabela")
public class Bean {
#DatabaseField(columnName = "campoid", generatedId = true)
private int _id;
#DatabaseField(columnName = "camponome")
private String nome;
#DatabaseField(foreign = true, columnName = "chave", canBeNull = false)
private Part chave;
}
and other classe mapped
#DatabaseTable(tableName = "other")
public class Part {
#DatabaseField(generatedId = true, columnName = "id")
private Integer id;
#DatabaseField(columnName = "texto")
private String texto;
}
but when i save an Bean object, the Object 'part' does not save too :(
Helper helper = OpenHelperManager.getHelper(this, Helper.class);
Dao<Bean, Integer> dao = helper.getDao(Bean.class);
Bean firstBean = new Bean();
firstBean.setNome("first be persisted");
Part part = new Part();
part.setTexto("ANY TEXT");
firstBean.setChave(part);
dao.create(firstBean);
in my log:
07-13 00:25:26.602: D/BaseMappedStatement(3796): insert data with statement 'INSERT INTO tabela (camponome ,chave ) VALUES (?,?)' and 2 args, changed 1 rows
any idea?
but when i save an Bean object, the Object 'part' does not save too :(
Right. ORMLite does not by default persist sub-objects when you create an object. You can turn on the foreignAutoCreate = true flag which will do that for you however. See the javadocs for foreignAutoCreate.
Your chave field should be defined then as:
#DatabaseField(foreign = true, columnName = "chave", canBeNull = false,
foreignAutoCreate = true)
private Part chave;
If you want to do it by hand, you should:
partDao.create(firstBean.chave);
beanDao.create(firstBean);
You create the Part in the database first because you need the ID from the Part which will be saved into your bean.
Related
I have this table which I would like to store different values as keys and vales:
#Entity
#Table(name = "wpf_payment_attributes")
public class WpfPaymentAttributes implements Serializable {
private static final long serialVersionUID = -2629784870868584850L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
#Column(length = 255)
private String name;
#Column(length = 255)
private String global_ley;
#Column(name = "VALUE", columnDefinition = "TEXT", length = 65535)
private String value;
....
}
WpfPaymentAttributes attibutes = new WpfPaymentAttributes();
attibutes.setName("usage");
attibutes.setValue("Test Usage");
attibutes.setGlobal_key(12333);
WpfPaymentAttributes attibutes = new WpfPaymentAttributes();
attibutes.setName("name");
attibutes.setValue("Peter");
attibutes.setGlobal_key(12333);
But how I can get all value with the same global key with one SQL query using JPA? The problem is that I don't know in advance what are the table columns and values.
I need to get this structure:
usage | name
-------------------
Test Usage | Peter
Is this possible with JPA?
This is not possible, since there are some issues that JPA won't be able to help you with:
there could be multiple WpfPaymentAttributes values with the same
global key and name (however, this could be solved by using a
database constraint);
there could be arbitrary values in the name
column, hence you'd have to make sure that they actually map to your expected result structure, there are no unknown "names" etc.
If you don't need a super-generic system, I'd advice you to write a simple mapper, that shouldn't be very complex. Just get all WpfPaymentAttributes by a specific global_key and apply the mapping. For example, here's the structure that you need:
public class Result {
private String usage;
private String name;
// ...
}
And then:
Result result = new Result();
List<WpfPaymentAttributes> attributes = entityManager.createQuery(
// query should be parameterized
"select a from WpfPaymentAttributes a where global_key = 12333"
).getResultList();
for (WpfPaymentAttributes attribute : attributes) {
String value = attribute.getValue();
switch(attribute.getName()) {
case "name":
result.setName(value);
break;
case "usage":
result.setUsage(value);
break;
default:
throw new IllegalStateException();
}
}
return result;
I searched a lot for this particular problem but i didn''t find any specific solution. I have a Composite Primary Key in one table and one of the field from this composite primary key is the part of the Composite Primary Key of another table. You can say that this particular field is the foreign key in the second table but i a not defining any exclusive Foreign Key constraint in the table definition. There can be multiple Records in the second table for each rec in the first table.i am trying to implement this using SPringBoot-JPA-Hibernate but not being able to do so. Can some body help me here. Here are the detais:-
I have a USER_CREDENTIAL table with following fields:-
CREATE TABLE `INSTITUTION_USER_CREDENTIAL` (
`INSTITUTION_USER_ID INT(10) NOT NULL, -> AutoGeneratd
`INSTITUTION_USER_NAME` VARCHAR(50) NOT NULL,
`INSTITUTION_USER_PASSWORD` VARCHAR(50) NOT NULL,
`FIRST_NAME` VARCHAR(100) NOT NULL,
`MIDDLE_NAME` VARCHAR(100),
`LAST_NAME` VARCHAR(100) NOT NULL,
PRIMARY KEY (`INSTITUTION_USER_ID`,`INSTITUTION_USER_NAME`)
);
2) Here is my second table
CREATE TABLE `INSTITUTION_USER_CREDENTIAL_MASTER` (
`INSTITUTION_ID` INT(10) NOT NULL, -> Autogenerated
`INSTITUTION_USER_ID` INT(10) NOT NULL, -> Coming from
INSTITUTION_USER_CREDENTIAL
`INSTITUTION_USER_ROLE` CHAR(02) NOT NULL,
`INSTITUTION_USER_STATUS` CHAR(02) NOT NULL,
`INSTITUTION_NAME` VARCHAR(200) NOT NULL,
`LAST_UPDT_ID` VARCHAR(100) NOT NULL,
`LAST_UPDT_TS` DATETIME NOT NULL,
PRIMARY KEY(`INSTITUTION_ID`,`INSTITUTION_USER_ID`,`INSTITUTION_USER_ROLE`)
);
Note that i haven't declare any particular foreign key in the second table. I have two #Embeddable Class corresponding to two primary key structure for two different table:-
For the INSTITUTION_USER_CREDENTIAL table:-
#Embeddable
public class InstitutionUserCredentialPrimaryKey implements Serializable{
private static final long serialVersionUID = 1L;
#Column(name = "INSTITUTION_USER_ID")
#GeneratedValue(strategy=GenerationType.AUTO)
private int institutionUserId;
#Column(name = "INSTITUTION_USER_NAME")
private String institutionUserName;
//Getter-Setters removed for clarity
}
Corresponding Entity Class:-
#Entity(name = "INSTITUTION_USER_CREDENTIAL")
public class InstitutionUserCredential {
#EmbeddedId
private InstitutionUserCredentialPrimaryKey
institutionUserCredentialPrimaryKey;
#Column(name = "INSTITUTION_USER_PASSWORD")
private String instituteUserPassword;
#Column(name = "FIRST_NAME")
private String firstname;
#Column(name = "MIDDLE_NAME")
private String middleName;
#Column(name = "LAST_NAME")
private String lastName;
#OneToMany(mappedBy="institutionUserCredential", cascade = CascadeType.ALL)
private List<InstitutionUserCredentialMaster>
institutionUserCredentialMaster;
//Getter-Setter and other part of the code removed for clarity
}
For the INSTITUTION_USER_CREDENTIAL_MASTER table:-
#Embeddable
public class InstituteUserCredentialMasterPrimaryKey implements Serializable
{
private static final long serialVersionUID = 1L;
#Column(name = "INSTITUTION_ID")
#GeneratedValue(strategy=GenerationType.AUTO)
private int institutionId;
#Column(name = "INSTITUTION_USER_ID")
private int institutionUserId;
#Column(name = "INSTITUTION_USER_ROLE")
private String userRole;
//Getter-Setter and other part of the code removed for clarity
}
Entity Class:-
#Entity(name = "INSTITUTION_USER_CREDENTIAL_MASTER")
public class InstitutionUserCredentialMaster {
#EmbeddedId
private InstituteUserCredentialMasterPrimaryKey
instituteUserCredentialMasterPrimaryKey;
#Column(name = "INSTITUTION_USER_STATUS")
private String userStatus;
#Column(name = "INSTITUTION_NAME")
private String institutionName;
#Column(name = "LAST_UPDT_ID")
private String lastUpdateId;
#Column(name = "LAST_UPDT_TS")
private String lastUpdateTimestamp;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="institutionUserId", referencedColumnName =
"INSTITUTION_USER_ID")
})
private InstitutionUserCredential institutionUserCredential;
//Getter-Setter and other part of the code removed for clarity
}
Note that only 1 field INSTITUTION_USER_ID, is getting used in the Composite PrimaryKey of the InstitutionUserCredentialMaster and is coming from the composite primary key of the InstitutionUserCredential.
When i am running my code this is giving me an error like :-
Invocation of init method failed; nested exception is
org.hibernate.AnnotationException:
referencedColumnNames(INSTITUTION_USER_ID) of com.bnl.application.entity.InstitutionUserCredentialMaster.institutionUserCredential referencing com.bnl.application.entity.InstitutionUserCredential not mapped to a single property
None of the examples i have seen so far involving the Composite Primary key and foreign key doesn't treat any one particular field and is more of the entire key structure. I am using MYSQL and i have checked that we can create table having composite primary key and one of the field from that composite key is foreign key in another table and also part of the Composite Primary key of the second table.
Any pointers appreciated
UPDATE:- In my first post i made a mistake while posting it. I am sorry that institutionUserName became a part of the InstitutionUserCredentialMaster. it was a typo. There is no existence of the intitutionUserName in the InstitutionUserCredentialMaster table. i have fixed that and updated the post.
***** Update based on the input by Niver and Wega *****
Update to the InstitutionUserCredentialMasterPrimaryKey
#Embeddable
public class InstituteUserCredentialMasterPrimaryKey implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "INSTITUTION_ID")
#GeneratedValue(strategy=GenerationType.AUTO)
private int institutionId;
#Column(name = "INSTITUTION_USER_ID")
private int institutionUserId;
// Added the institutionUserName
#Column(name = "INSTITUTION_USER_NAME")
private String institutionUserName;
#Column(name = "INSTITUTION_USER_ROLE")
private String userRole;
}
Update to the Entity Class InsstitutionUserCredentialMaster :-
#Entity(name = "INSTITUTION_USER_CREDENTIAL_MASTER")
public class InstitutionUserCredentialMaster {
#EmbeddedId
private InstituteUserCredentialMasterPrimaryKey instituteUserCredentialMasterPrimaryKey;
#Column(name = "INSTITUTION_USER_STATUS")
private String userStatus;
#Column(name = "INSTITUTION_NAME")
private String institutionName;
#Column(name = "LAST_UPDT_ID")
private String lastUpdateId;
#Column(name = "LAST_UPDT_TS")
private String lastUpdateTimestamp;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="institutionUserId", referencedColumnName = "INSTITUTION_USER_ID"),
#JoinColumn(name="institutionUserName",referencedColumnName = "INSTITUTION_USER_NAME")
})
private InstitutionUserCredential institutionUserCredential;
}
This time i am getting an error like
Invocation of init method failed; nested exception is org.hibernate.DuplicateMappingException: Table [institution_user_credential_master] contains physical column name [institution_user_id] referred to by multiple physical column names: [institutionUserId], [INSTITUTION_USER_ID]
I think that the problem is that you are not referencing the other part of the EmbeddedId in the JoinColumns annotation. You have defined that also the institutionUserName is part of the primary key, so you should mention it as well in the definition of the foreign key in entity InstitutionUserCredentialMaster.
I having issues in mapping a mysql SET type to Java Set using JPA
To illustrate my question i frame a random example below
Here is a table which has a column genre which is of type Set (i.e:it will be a collection of Strings)
CREATE TABLE `MusicCD` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`period` ENUM('Classical', 'Modern','Antique') NOT NULL,
`genre` SET('horror','thriller','comedy','drama','romance') ,
PRIMARY KEY (`id`)
)
Below is the entity class used for the mapping
#Entity
#Table(name = "MusicCD")
class MusicCD {
private long id;
private Period period;
private Set<String> genre;
//other getter setters //
#Column(name = "genre")
#ElementCollection(targetClass = String.class, fetch = FetchType.EAGER)
public Set<String> getGenre() {
return genre;
}
public void setGenre(Set<String> genre) {
this.genre = genre;
}
}
With this mapping there is no exception but the set is empty in the entity object because the get query sent by JPA/hibernate sents query for all fields in table MusicCD but for the genre it sends a separate query to table MusicCD_genre
When i see the sql schema there is a autogenerated table MusicCD_genre which is empty.
Sending a sql select query for genre on MusicCD returns the genres.
So how does the Set data type in sql work and what is the correct annotation to map it?
Update:
I also tried
#TypeDefs({#TypeDef(name = "javaSet", typeClass = HashSet.class)})
and annotate the getter with
#Type(type = "javaSet")
but this doesn't work with EOFException during de-serialization.
This might work by replacing the HashSet with correct type to deserialize to.
I know it's an old question, but I prefer treat these ´MySQL special type´ columns in the getters/setters when the most use of them would be in java code.
#Entity
#Table(name = "MusicCD")
class MusicCD {
/*...*/
#Column(name = "genre")
private String genreStr;
/*...*/
public Set<String> getGenre() {
if(genreStr == null)
return Collections.emptySet();
else
return Collections.unmodifiableSet(
new HashSet<String>(Arrays.asList(genreStr.split(",")))
);
}
public void setGenre(Set<String> genre) {
if(genre == null)
genreStr = null;
else
genreStr = String.join(",", genre);
}
}
I use the immutable version of Set, because that avoids trying alter the set values without actually alter the DB.
I have a simple model in Play Framework 2, and I would like to specify a default value to be inserted on a specify INT column if none is provided when the INSERT is performed.
Model:
#Entity
#Table(name = "DashboardOptions", schema = "dbo")
public class DashboardOptions extends Model implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
public Long id;
#Basic(optional = false)
#Column(name = "userId")
public Long userId;
#Column(name = "chartType")
public String chartType;
public String name;
public Integer size = 2;
I'd like to have the size column populate with 2 by default, however, if I specify the default value as above, my database evolution does not reflect this:
create table dbo.DashboardOptions (
id numeric(19) identity(1,1) not null,
userId numeric(19) not null,
chartType varchar(255),
name varchar(255),
size integer,
constraint pk_DashboardOptions primary key (id))
;
What I would expect to see is this:
create table dbo.DashboardOptions (
id numeric(19) identity(1,1) not null,
userId numeric(19) not null,
chartType varchar(255),
name varchar(255),
size integer default 2,
constraint pk_DashboardOptions primary key (id))
;
Use own columnDefinition like this:
#Column(columnDefinition = "integer default 2")
public Integer size = 2;
Another option is to use #PrePersist tag package javax.persistence. you can have a method decorated in your bean with #PrePersist and that method is called before Ebean.save call. so in this case the following code would set the default value of size to 2.
#PrePersist
protected void onCreate {
if (this.size == null)
this.size = 2;
}
This approach is applicable only within the context of ORM (Ebean) and obviously wouldn't work directly with SQL. The advantage of this method is that this is more database neutral in the sense that integer default 2 might not be a valid column definition string in some unknown strange RDBMS systems.
here is a snippet of my entity (it also has hashcode and equals created which are the default ones generated by java
#Entity
#Table(name = "media_tspec_external_registry")
public class Registry implements Serializable {
public Registry() {
//for hibernate :D
}
public Registry(String show, String protocol, String externalEndpoint, String userURI, String version) {
super();
this.show = show;
this.protocol = protocol;
this.externalEndpoint = externalEndpoint;
this.userURI = userURI;
this.version = version;
}
#Id
#Column(name = "show", nullable = false)
private String show;
#Id
#Column(name = "protocol", nullable = false)
private String protocol;
#Column(name = "external_endpoint", nullable = true)
private String externalEndpoint;
here is my method which is trying to load an entity which does not exist, based on these key values
Registry reg = new Registry("invalid", "idvalue", null, null, null);
Registry reg2 = null;
try {
reg2 = (Registry) session.load(Registry.class, reg);
} catch (HibernateException e) {
throw new UserException("A registry entry does not exist for this show: " + show + " and protocol: " + protocol);
}
it never throws the exception and reg2 is now set to a registry object with all the fields set to null.
i have also noted that the load will not even load a existing entity.
however if i use get instead it works as expected (loading valid object returning null for non existing objects)
reg2 = (Registry) session.get(Registry.class, reg);
any explanation would be appreciated.
Thanks
this is expected behavior. session.load is meant to get an object which can be used to satisfy references, e.g.
User u = new User();
u.setRole((Role)session.load(Role.class, 5));
session.save(u);
load will not generate a roundtrip. If there is no reference to the object available it will create a proxyobject or in your case will recycle your compositekey object and relies on you that the entity exists since it can not ask the database if it is so.