Decide to use sqlite with hibernate.
First of all this is only test project.
My entity classes
Address.java
#Entity
#Table(name = "ADDRESS")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", length = 3, nullable = false, unique = true)
private Long id;
#Column(name = "NAME", length = 40, nullable = false)
private String name;
#OneToMany(mappedBy = "address", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Flat> flats;
Flat
#Entity
#Table(name="FLAT")
public class Flat {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", length = 6, unique = true)
private long id = 1L;
#Column(name = "CRTN_DATE", nullable = false)
private Date creationDate;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "ADDRESS_ID", nullable = false)
private Address address;
#Column(name = "ROOM_CNT", length = 1)
private int roomCount;
#Column(name = "AREA", length = 4)
private long totalArea;
#Column(name = "PHONE", length = 1)
private String phone;
#Column(name = "PRICE", length = 7)
private float price;
so my problem:
When I try to insert new row like this
Flat flat = new Flat();
flat.setTotalArea(45);
flat.setCreationDate(new Date());
flat.setAddress(addressDAO.getByID(3));
flat.setRoomCount(2);
flat.setPrice(68000);
flat.setPhone("Y");
flat.setImages(new HashSet<ImageStorage>());
flatDAO.save(flat);
I get such output in my console:
Hibernate: insert into FLAT (ADDRESS_ID, CRTN_DATE, PHONE, PRICE, ROOM_CNT, AREA) values (?, ?, ?, ?, ?, ?)
ERROR: [SQLITE_CONSTRAINT] Abort due to constraint violation (NOT NULL constraint failed: FLAT.ID)
as I understand Hibernate didn't add null field (ID) into generated sql query. So maybe someone know how to fix this?
thanks in advance.
UPDATE
dialect I copied from here
dll
CREATE TABLE ADDRESS
(
ID INTEGER PRIMARY KEY NOT NULL,
NAME TEXT NOT NULL
);
CREATE TABLE FLAT
(
ID INTEGER PRIMARY KEY NOT NULL,
CRTN_DATE TEXT NOT NULL,
ADDRESS_ID INTEGER NOT NULL,
ROOM_CNT INTEGER NOT NULL,
AREA REAL NOT NULL,
PHONE TEXT NOT NULL,
PRICE REAL NOT NULL,
FOREIGN KEY (ADDRESS_ID) REFERENCES ADDRESS (ID) DEFERRABLE INITIALLY DEFERRED
);
If you define your ID column as INTEGER PRIMARY KEY you can omit the ID column in your insert statement (which hibernate does in your case).
Take a look at Autoincrement In SQLite
Try this in SQL fiddle (select sqlite database at the top)
Schema
CREATE TABLE flat(id INTEGER PRIMARY KEY, name TEXT);
SQL
INSERT INTO flat(name) VALUES('flat 1');
INSERT INTO flat(id, name) VALUES(2, 'flat 2');
INSERT INTO flat(name) VALUES('flat 3');
select * from flat;
UPDATE
I've never used sqlite but according to the example above your code should work. You've defined your id column as INTEGER PRIMARY KEY and hibernate doesn't include the id column in it's inserts.
Are you sure you haven't enabled "hibernate.hbm2ddl.auto"? The error makes sense (to me at least) if you are generating your tables with hibernate as your dialect isn't using the INTEGER PRIMARY KEY clause for your identity columns: take a look at hasDataTypeInIdentityColumn() and getIdentityColumnString.
please add your custom dialect
public class SQLiteDialect extends Dialect {
public SQLiteDialect() {
registerColumnType(Types.BIT, "integer");
registerColumnType(Types.TINYINT, "tinyint");
registerColumnType(Types.SMALLINT, "smallint");
registerColumnType(Types.INTEGER, "integer");
registerColumnType(Types.BIGINT, "bigint");
registerColumnType(Types.FLOAT, "float");
registerColumnType(Types.REAL, "real");
registerColumnType(Types.DOUBLE, "double");
registerColumnType(Types.NUMERIC, "numeric");
registerColumnType(Types.DECIMAL, "decimal");
registerColumnType(Types.CHAR, "char");
registerColumnType(Types.VARCHAR, "varchar");
registerColumnType(Types.LONGVARCHAR, "longvarchar");
registerColumnType(Types.DATE, "date");
registerColumnType(Types.TIME, "time");
registerColumnType(Types.TIMESTAMP, "timestamp");
registerColumnType(Types.BINARY, "blob");
registerColumnType(Types.VARBINARY, "blob");
registerColumnType(Types.LONGVARBINARY, "blob");
registerColumnType(Types.BLOB, "blob");
registerColumnType(Types.CLOB, "clob");
registerColumnType(Types.BOOLEAN, "integer");
}
public IdentityColumnSupport getIdentityColumnSupport() {
return new SQLiteIdentityColumnSupport();
}
public boolean hasAlterTable() {
return false;
}
public boolean dropConstraints() {
return false;
}
public String getDropForeignKeyString() {
return "";
}
public String getAddForeignKeyConstraintString(String constraintName, String[] foreignKey, String referencedTable, String[] primaryKey, boolean referencesPrimaryKey) {
return "";
}
public String getAddPrimaryKeyConstraintString(String constraintName) {
return "";
}
public String getForUpdateString() {
return "";
}
public String getAddColumnString() {
return "add column";
}
public boolean supportsOuterJoinForUpdate() {
return false;
}
public boolean supportsIfExistsBeforeTableName() {
return true;
}
public boolean supportsCascadeDelete() {
return false;
}
``````
---------------------------------------------------
public class SQLiteIdentityColumnSupport extends IdentityColumnSupportImpl {
#Override
public boolean supportsIdentityColumns() {
return true;
}
#Override
public String getIdentitySelectString(String table, String column, int type)
throws MappingException {
return "select last_insert_rowid()";
}
#Override
public String getIdentityColumnString(int type) throws MappingException {
return "integer";
}
add the property in properties
hibernate.dialect=com.common.config.db.SQLiteDialect // as per you class package
then then configure your hibernate properties with dialect
("hibernate.dialect", dialect);
Related
I have the script which to create stored function.
CREATE OR REPLACE TYPE OBJ_EMPL AS OBJECT
(
employeeID NUMBER(19,0),
address VARCHAR2(100 CHAR),
first_Name VARCHAR2(50 CHAR),
last_Name VARCHAR2(50 CHAR)
);
CREATE OR REPLACE TYPE LIST_OBJ_EMPL IS TABLE OF OBJ_EMPL;
CREATE OR REPLACE FUNCTION GET_ALL_EMPLOYEES RETURN LIST_OBJ_EMPL IS
varObjEmpl LIST_OBJ_EMPL;
BEGIN
SELECT OBJ_EMPL (
employeeID,
first_Name,
last_Name,
address
)
BULK COLLECT INTO varObjEmpl
FROM EMPLOYEES;
RETURN varObjEmpl;
EXCEPTION
WHEN NO_DATA_FOUND THEN
return NULL;
WHEN OTHERS THEN
-- Consider logging the error and then re-raise
RAISE;
END GET_ALL_EMPLOYEES;
/
--SELECT * FROM TABLE(getListEmployees);
/*
CREATE TABLE EMPLOYEES
(EMPLOYEEID NUMBER(19,0) NOT NULL ENABLE,
ADDRESS VARCHAR2(100 CHAR),
FIRST_NAME VARCHAR2(50 CHAR),
LAST_NAME VARCHAR2(50 CHAR),
CONSTRAINT PK_EMPLOYEEID PRIMARY KEY (EMPLOYEEID)
);
*/
Entity
#Entity
#Table(name = "employees")
#NamedStoredProcedureQueries({
#NamedStoredProcedureQuery(name = "getListEmployees",
procedureName = "get_all_employees",
resultClasses = Employee.class)
})
public class Employee {
#Id
#SequenceGenerator(name = "jpaSequence.Employee",
sequenceName = "SEQUENCE_EMPLOYEE",
allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "jpaSequence.Employee")
#Column(name = "employeeID")
private Long employeeID;
#Column(name = "first_Name")
#Size(message = "Field 'firstName' can't be более {} ",max = 50)
private String firstName;
#Column(name = "last_Name")
#Size(message = "Field 'firstName' can't be более {} ",max = 50)
private String lastName;
#Column(name = "address")
#Size(message = "Field 'firstName' can't be более {} ",max = 100)
private String address;
public Employee() {
}
repository
public interface EmployeesRepository
extends CrudRepository<Employee, Long>,
EmployeesRepositoryCustom {
}
public interface EmployeesRepositoryCustom {
List<Employee> getAllEmployees();
}
#Repository
public class EmployeesRepositoryImpl implements EmployeesRepositoryCustom {
#PersistenceContext
private EntityManager entityManager;
#Override
public List<Employee> getAllEmployees() {
StoredProcedureQuery getAllEmployees =
entityManager.createNamedStoredProcedureQuery("getListEmployees");
return getAllEmployees.getResultList();
}
}
When I try to get data I have a exception.
Error calling CallableStatement.getMoreResults; SQL
[get_all_employees]; nested exception is
org.hibernate.exception.SQLGrammarException: Error calling
CallableStatement.getMoreResults
java.sql.SQLException: ORA-06550: Row 1, column 7: PLS-00221:
'GET_ALL_EMPLOYEES' is not a procedure or is not defined ORA-06550:
Row 1, column 7: PL/SQL: Statement ignored
Maybe you can't use #NamedStoredProcedureQuery annotations to work with stored functions ?
I don't know what to do. Who has any ideas ?
Solution
public interface EmployeesRepository
extends CrudRepository<Employee, Long>,
EmployeesRepositoryCustom {
String query = "select * from table (get_all_employees)";
#Query(nativeQuery = true, value = query)
Iterable<Employee> findAllEmployees();
}
Unable to find column with logical name: VERTICAL_MARKET_ID in org.hibernate.mapping.Table(bck_vertical_market) and its related supertables and secondary tables
at org.hibernate.cfg.Ejb3JoinColumn.checkReferencedColumnsType(Ejb3JoinColumn.java:582)
Can anyone help with this fail? None of existing posts help me. My classes which uses VerticalMarket looks like:
#Entity
#Table(name = "BCK_VERTICAL_MARKET")
public class VerticalMarketEntity implements Serializable {
private VerticalMarketID verticalMarketId;
private String name;
public VerticalMarketEntity() {
}
public VerticalMarketEntity(VerticalMarketID verticalMarketId) {
if (Assert.CHECK)
Assert.notNull(verticalMarketId, "Parameter for id must be set");
this.verticalMarketId = verticalMarketId;
}
#EmbeddedId
#AttributeOverride(name = "verticalMarketId", column = #Column(name = "VERTICAL_MARKET_ID", nullable = false, length = 100))
#Attribute(index = 0, primaryKey = true)
public VerticalMarketID getVerticalMarketId() {
return verticalMarketId;
}
#Attribute(index = 1, type = String100TD.class)
#Column(name = "NAME", length = 100)
#Basic
public String getName() {
return name;
}
}
#Entity
#Table(name = "BCK_CERTIFICATE")
public class CertificateEntity {
private VerticalMarketEntity verticalMarket;
#Relation(index = 2, target = VerticalMarketEntity.class)
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "VERTICAL_MARKET", referencedColumnName = "VERTICAL_MARKET_ID")
public VerticalMarketEntity getVerticalMarket() {
return verticalMarket;
}
#Entity
#Table(name = "BCK_OFFERED_SERVICE")
public class OfferedServiceEntity implements Serializable {
private VerticalMarketEntity verticalMarket;
#Relation(index = 2, target = VerticalMarketEntity.class)
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "VERTICAL_MARKET", referencedColumnName = "VERTICAL_MARKET_ID")
public VerticalMarketEntity getVerticalMarket() {
return verticalMarket;
}
}
CREATE TABLEBCK_VERTICAL_MARKET (
VERTICAL_MARKET_ID CHAR(36) NOT NULL,
NAME VARCHAR2(100 CHAR) NOT NULL
)
ALTER TABLE BCK_VERTICAL_MARKET ADD CONSTRAINT PK_VERTICAL_MARKET PRIMARY KEY (VERTICAL_MARKET_ID);
CREATE TABLEBCK_CERTIFICATE (
CERTIFICATE_ID CHAR(36) NOT NULL,
IS_OTHER NUMBER(1) NOT NULL,
VERTICAL_MARKET CHAR(36) NOT NULL,
NAME VARCHAR2(100 CHAR) NOT NULL
);
ALTER TABLE BCK_CERTIFICATE ADD CONSTRAINT PK_CERTIFICATE PRIMARY KEY (CERTIFICATE_ID);
ALTER TABLE BCK_CERTIFICATE ADD CONSTRAINT FK__C_VERTICAL_MARKET_ID
FOREIGN KEY (VERTICAL_MARKET) REFERENCES BCK_VERTICAL_MARKET (VERTICAL_MARKET_ID);
CREATE TABLE BCK_OFFERED_SERVICE (
OFFERED_SERVICE_ID CHAR(36) NOT NULL,
VERTICAL_MARKET CHAR(36) NOT NULL,
OFFERED_SERVICE_TYPE CHAR(36)
) ;
ALTER TABLE BCK_OFFERED_SERVICE ADD CONSTRAINT PK_OFFERED_SERVICES PRIMARY KEY (OFFERED_SERVICE_ID);
ALTER TABLE BCK_OFFERED_SERVICE ADD CONSTRAINT FK___O_S_VERTICAL_MARKET_ID
FOREIGN KEY (VERTICAL_MARKET) REFERENCES BCK_VERTICAL_MARKET (VERTICAL_MARKET_ID);
ALTER TABLE BCK_OFFERED_SERVICE ADD CONSTRAINT FK___O_S_T_ID
FOREIGN KEY (OFFERED_SERVICE_TYPE) REFERENCES BCK_OFFERED_SERVICE_TYPE (OFFERED_SERVICE_TYPE_ID);
I am using envers in my project to audit data.
Now I want to access changed data with audit query.
My pojo class for table is below
#Entity
#Audited(withModifiedFlag=true)
#Table(name = "INSTRUMENT", uniqueConstraints = #UniqueConstraint(columnNames = "INSTRUMENT_NAME"))
public class Instrument implements java.io.Serializable {
private long instrumentId;
private String instrumentName;
private WorkflowState workflowState;
#Id
#Column(name = "INSTRUMENT_ID", unique = true, nullable = false, precision = 22, scale = 0)
public long getInstrumentId() {
return this.instrumentId;
}
public void setInstrumentId(long instrumentId) {
this.instrumentId = instrumentId;
}
#Column(name = "INSTRUMENT_NAME", unique = true, nullable = false, length = 50)
public String getInstrumentName() {
return this.instrumentName;
}
public void setInstrumentName(String instrumentName) {
this.instrumentName = instrumentName;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "STATUS", nullable = false)
public WorkflowState getWorkflowState() {
return this.workflowState;
}
public void setWorkflowState(WorkflowState workflowState) {
this.workflowState = workflowState;
}
}
Now I tried to access this with audit query as
AuditQuery query = reader.createQuery().forRevisionsOfEntity(Instrument.class, false, true)
.add(AuditEntity.property("status").hasChanged());
List list= query.getResultList();
So at the time of accessing getResultList() , Its throwing Exception as follows
SqlExceptionHelper: Fail to convert to internal representation
I figured it out, this is because in my db Instrument.status column is as String data Type. While here I am using Join.
So please tell me how to write query to resolve this problem
PROBLEM is How to write Audit Query if my table has foreign key (class property have join dependency).
Join table WorkflowState discription is as follows
public class WorkflowState implements java.io.Serializable {
private BigDecimal stateId;
private Workflow workflow;
private String stateName;
//getters and setters
And it has a join column too i.e "workflow" .
Use workflowState rather than status. The API is based on you specifying the property name and not the column name.
I have 1:m relation. I post data about "1" and also about "m" relation in one post. What i am trying to achieve is to insert data ( m ) into "1" , then persist 1 into database which should create info in database about 1 and about m.
The "1" Enitity:
private List<OptionEntity> options;
#OneToMany(mappedBy = "survey", cascade = CascadeType.MERGE)
public List<OptionEntity> getOptions() {
return options;
}
public void setOptions(List<OptionEntity> options) {
this.options= options;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "survey_id", nullable = false)
public int getSurveyId() {
return surveyId;
}
public void setSurveyId(int surveyId) {
this.surveyId = surveyId;
}
the "m" entitites
private SurveyEntity survey;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="survey_id")
public SurveyEntity getSurvey() {
return survey;
}
public void setSurvey(SurveyEntity survey ) {
this.survey = survey;
}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "option_id", nullable = false)
public int getOptionId() {
return optionId;
}
public void setOptionId(int optionId) {
this.optionId = optionId;
}
However when i do
List<OptionEntity> ops = new ArrayList<>();
for( String option : options ){
OptionEntity tmp_option = new OptionEntity();
tmp_option.setText( option );
ops.add(tmp_option);
}
survey.setOptions(ops);
surveyDAO.add(survey);
when add is
public void add ( SurveyEntity s )
{
em.persist( s );
}
Creates only record for "1" entity in database. The records for all "m" entities are not inserted in the databases.
I thought whats important here is identity set to AUTO for m entities so database can create their id ( it has autoincrement ).
Seems i am wrong in this one.
What is the correct way to insert into 1:m relation at once?
Thanks for help
You have to do two things:
1) Set the relationship on both sides, so in the loop add the Survey entity to each of the Option entities:
for( String option : options ){
OptionEntity tmp_option = new OptionEntity();
tmp_option.setText( option );
ops.add(tmp_option);
tmp_option.setSurvey(survey);
}
2) Either use em.merge() instead of em.persist() or add this cascade option:
#OneToMany(mappedBy = "survey", cascade = {CascadeType.MERGE, CascadeType.PERSIST})
public List<OptionEntity> getOptions() {
return options;
}
I am working on an authentication model that would suit GlassFish's JDBC Realm requirements.
In this model I have one group which can contain multiple users, but each user can only be in one group (e.g. su, admin, etc.).
I have two entities: Groups.java (for groups) and Credential.java (for users) and intend to feed the generated join table to Glassfish's "Group Table" property.
I am able to persist both Groups and Credential instances, but the required middle table (credential_groups) is not even created, let alone updated.
Below are my entities:
Credential.java:
#Entity
#Access(AccessType.PROPERTY)
#Table(name = "credential")
public class Credential extends MetaInfo implements Serializable {
private String username;
private String passwd;
private Groups group;
private boolean blocked;
private boolean suspended;
public Credential() {
super();
}
public Credential(String createdBy) {
super(Instant.now(), createdBy);
}
public Credential(String createdBy, String username, String passwd) {
this(createdBy);
this.username = username;
this.passwd = passwd;
}
#Id
#Column(name = "username")
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
updateModified();
}
#Column(name = "passwd")
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
updateModified();
}
#ManyToOne(fetch = FetchType.LAZY)
public Groups getGroups() {
return group;
}
public void setGroups(Groups group) {
this.group = group;
group.getCredentials().add(this);
updateModified();
}
#Column(name = "is_blocked")
public boolean isBlocked() {
return blocked;
}
public void setBlocked(boolean blocked) {
this.blocked = blocked;
updateModified();
}
#Column(name = "is_suspended")
public boolean isSuspended() {
return suspended;
}
public void setSuspended(boolean suspended) {
this.suspended = suspended;
updateModified();
}
}
Groups.java:
#Entity
#Access(AccessType.PROPERTY)
#Table(name = "groups")
#NamedQueries({
#NamedQuery(name = "findAllGroups",
query = "SELECT g FROM Groups g order by g.modifiedDate DESC")
})
public class Groups extends MetaInfo implements Serializable {
private String groupName;
private Set<Credential> credentials;
public Groups() {
super();
credentials = new HashSet();
}
public Groups(String groupName) {
this();
this.groupName = groupName;
}
public Groups(String createdBy, String groupName) {
this();
setCreatedBy(createdBy);
this.groupName = groupName;
}
#Id
#Column(name = "group_name")
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
updateModified();
}
#OneToMany(mappedBy = "groups")
#JoinTable(
name = "credential_groups",
joinColumns = #JoinColumn(name = "group_name"),
inverseJoinColumns = #JoinColumn(name = "username")
)
public Set<Credential> getCredentials() {
return credentials;
}
public void setCredentials(Set<Credential> credentials) {
this.credentials = credentials;
}
public void addCredential(Credential c) {
credentials.add(c);
if (c.getGroups() != this) {
c.setGroups(this);
}
}
}
As I said persisting both works (for Groups I have also tried updating, querying), but this error keeps on firing on every operation with either entity:
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Can't write; duplicate key in table '#sql-4d2_54'
Error Code: 1022
Call: ALTER TABLE credential ADD CONSTRAINT FK_credential_GROUPS_group_name FOREIGN KEY (GROUPS_group_name) REFERENCES groups (group_name)
Query: DataModifyQuery(sql="ALTER TABLE credential ADD CONSTRAINT FK_credential_GROUPS_group_name FOREIGN KEY (GROUPS_group_name) REFERENCES groups (group_name)")
Important update:
This is the error that is being thrown even before the pasted above exception:
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'group VARCHAR(255) NOT NULL, PRIMARY KEY (credential, group))' at line 1
Error Code: 1064
Call: CREATE TABLE credential_groups (credential VARCHAR(255) NOT NULL, group VARCHAR(255) NOT NULL, PRIMARY KEY (credential, group))
As requested by Francois Motard, here's my jUnit method that triggers the error (the group is created in another test class):
public class CredentialRepositoryTests {
private final OCGroupsRepository groupRepo;
private final OCCredentialRepository credentialRepo;
public CredentialRepositoryTests() {
groupRepo = new OCGroupsRepository();
credentialRepo = new OCCredentialRepository();
}
...
#Test
public void createCredentialTest(){
//retrieve the group
Groups admin = groupRepo.getByGroupName("admin");
Credential rimma = new Credential("user_creator", "sample_user", "abc123");
admin.addCredential(rimma);
assertTrue(groupRepo.updateGroups(admin));
}
I based my code on the instructions from the EJB 3.0 in Action by Manning and tried to resolve the join table issue based on this stackoverflow answer: How to create join table with JPA annotations?
Can anyone help me have the join table created and updated? Thank you very much.
Resolved by removing the mappedBy attribute from the #OneToMany annotation of the owned entity (this fixed the non-creation of the join table) and by adding the unique attribute (set to true - this solved the "PRIMARY KEY (credential, group)" issue) to the #JoinColumn annotation inside the #JoinTable of the same property:
#OneToMany
#JoinTable(
name = "credential_groups",
joinColumns = #JoinColumn(name = "group_name"),
inverseJoinColumns = #JoinColumn(name = "username", unique=true)
)
public Set<Credential> getCredentials() {
return credentials;
}
Main take away don't ever ever combine a "mappedBy" with the #JoinTable in a ManyToOne/OneToMany entities pair.