I have a MySQL table:
mysql> show create table items\G
*************************** 1. row ***************************
Table: items
Create Table: CREATE TABLE `items` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
1 row in set (0.02 sec)
I create new rows from a Java program via an Entity class:
#Entity
#Table(name = "items", schema = "office_db")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Items.findAll", query = "SELECT i FROM Items i"),
#NamedQuery(name = "Items.findById", query = "SELECT i FROM Items i WHERE i.id = :id"),
#NamedQuery(name = "Items.findByName", query = "SELECT i FROM Items i WHERE i.name = :name"),
#NamedQuery(name = "Items.findByCreated", query = "SELECT i FROM Items i WHERE i.created = :created")
})
public class Items implements Serializable {
#Column(name = "name",length = 128)
private String name;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Basic(optional = false)
#Column(name = "created")
#Temporal(TemporalType.TIMESTAMP)
private Date created;
#OneToMany(mappedBy = "itemId")
private Collection<Documents> documentsCollection;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "items")
private Collection<ItemAttributes> itemAttributesCollection;
(more stuff ...)
I only set the NAME column, and as expected, the ID and CREATED are set by default:
mysql> select * from items;
+----+--------+---------------------+
| id | name | created |
+----+--------+---------------------+
| 2 | Case 2 | 2017-10-31 13:47:52 |
| 3 | Case 3 | 2017-10-31 13:48:02 |
+----+--------+---------------------+
2 rows in set (0.00 sec)
However, when I reload the table into Java later in the same session:
public List<Items>findItems(){
TypedQuery<Items> query=
em.createNamedQuery("Items.findAll",Items.class);
return query.getResultList();
}
the ID column is loaded correctly, but the CREATED column comes up as blank. CREATED shows up correctly if I relaunch the application (this runs on a glassfish server). My guess is that the reason for this difference is the #GeneratedValue annotation on ID, but I can't apply it on the CREATED field, it seems, or at least not naively. What is the correct way to make the generated timestamp load?
The answer to my conundrum, it appears, is to call em.refresh(), according to this: Invalidating JPA EntityManager session - this is for Hibernate, but it seems to be the same for EclipseLink. I run in to an exception, in my case, but I will post that in another question.
Related
I am using java hibernate to store my data entities. I want to know the sql command to select the data in a #ManyToMany column using postgresql-psql command.
for normal columns, I can just run:
SELECT id FROM university;
But now I have the following university entity:
#Entity
#Table(name = "university")
public class University {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private Long id;
#ManyToMany(fetch=FetchType.EAGER)
#JoinColumn(name="students" /* referencedColumnName="id" */)
private List<Student> students;
}
#Entity
#Table(name = "student", uniqueConstraints = { #UniqueConstraint(columnNames={"name"})})
public class Student
{
#Id
private Long id;
#NotBlank
#NotNull
private String name;
}
The problem is, I don't know what the student list is called in psql.
When I run:
SELECT students FROM university;
I get:
ERROR: column "students" does not exist
When I type:
\d university
I get (not actual data: data anonymized to student/university example):
Column | Type | Collation | Nullable | Default
------------------+-----------------------------+-----------+----------+---------
id | bigint | | not null |
Indexes:
"university_pkey" PRIMARY KEY, btree (id)
"uk_rwpd2frv6wtkgqtxn3envk3i8" UNIQUE CONSTRAINT, btree (name)
Referenced by:
TABLE "university_students" CONSTRAINT "fkdkjk4jgutu64g937gkknybax2" FOREIGN KEY (university) REFERENCES university(id)
You have a table 'university_students', can you do 'select * from university_students'
I think you have this structure :
student:
id
name
0
first_student
university:
id
3
university_students:
university_id
students_id
3
0
So the only think you need to do is this :
SELECT * FROM student WHERE id IN (SELECT students_id FROM university_students WHERE university_id = 3)
That will search all students_id where university_id is equal to 3 in the table university_students, all that will match with the table student.
if you only want their name replace * by name like this :
SELECT name FROM student WHERE id IN (SELECT students_id FROM university_students WHERE university_id = 3)
OK got what I wanted, inspired by: https://stackoverflow.com/a/3486662/2396744
Given:
University:
database=> SELECT id FROM university;
id
----
2
3
4
5
(4 rows)
Students:
database=> SELECT id,name FROM students;
id | name
----+----------
4 | Jack
3 | Jill
2 | Jonas
(3 rows)
university_students:
database=> SELECT * FROM university_students;
university_id | students_id
---------------+---------
3 | 3
3 | 2
4 | 4
4 | 2
5 | 3
5 | 2
(6 rows)
The query becomes:
database=> SELECT u.id,us.university_id,us.students_id,s.name
FROM students as s, university as u, university_students as us
WHERE u.id = 5
AND us.university_id = u.id
AND us.student_id = s.id;
id | university_id | student_id | name
----+---------------+------------+----------
5 | 5 | 3 | Jill
5 | 5 | 2 | Jonas
(2 rows)
You can and you really should learn to use JpaRepository of java spring boot, it's easy to use and you can do lot of thing with it, for example i have this class : Client.java
#Entity
public class Client {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
#Column(unique = true, nullable = false) private String email, username;
#Column(unique = true) private String phone;
#Column(nullable = false) private String password, firstName, lastName;
private boolean active;
private long timestampCreation;
private double coefficientReducingBuyer;
private long score;
// User privileges, roles, Address
#ManyToMany(mappedBy = "clients", fetch = FetchType.EAGER) private Set<Privilege> privileges;
#ManyToOne(fetch = FetchType.EAGER) private Role role;
#OneToMany(mappedBy = "client", fetch = FetchType.EAGER, cascade = CascadeType.ALL) private Set<AddressClient> addressClients;
// Market
#OneToMany(fetch = FetchType.EAGER) private Set<Order> orders;
#OneToOne(mappedBy = "client", cascade = CascadeType.ALL) private ShoppingCart shopping;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) private Set<Favorite> favorites;
}
public interface ClientRepository extends JpaRepository<Client, Integer> {
// Find all client by phone number
Client findByPhone(String phone);
// Find all client by phone number which is containing some element of my string
List<Client> findByPhoneIsContaining(String phone);
// Same but ignoring capital letter a and A is the same here
List<Client> findByPhoneIgnoreCaseIsContaining(String phone);
// Find by other table
List<Client> findByRole(Role role);
// Find by other table with a specific value in it
Set<Client> findByPrivileges_Name(String privilege);
}
You don't need to add #Table, only if you want to change the name of your table.
Here my table will name client but i can change is name to user when i do this : #Table(name="user"), so don't use #Table when you don't need it.
I'm Developing small MicroService Using Spring boot.
My database Is Postgres and I have a pojo in my service mapping the table.
Initially my Table is created with one Primary Key NOT NULL serial.
Now I want to add One more column with serial number starting from 6000 and map with my pojo.
#JsonProperty("id")
#Column(name = "id", nullable = false)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY,generator = "order_seq_gen")
#SequenceGenerator(name = "order_seq_gen", sequenceName ="order_id_seq")
private Integer id;
#JsonProperty("state")
private String state;
#JsonProperty("user_Id")
#Column(name = "user_Id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY,generator = "user_seq_gen")
#SequenceGenerator(name = "user_seq_gen", sequenceName ="user_id_seq")
private Integer userId;
This is My Pojo.
The first Id Is creating but the userId Is not creating.
Bellow is my Table structure and sequences.
This is my create table statement
CREATE TABLE public.user
(
id integer NOT NULL DEFAULT nextval('user_id_seq'::regclass),
created timestamp without time zone,
modified timestamp without time zone,
state character varying(255),
name text,
user_id integer NOT NULL DEFAULT nextval('user_id_seq'::regclass), CONSTRAINT user_pkey
PRIMARY KEY (id)
)
Below is my sequence.
CREATE SEQUENCE public.user_id_seq INCREMENT 1 MINVALUE 6001
MAXVALUE 9223372036854775807 START 6000 CACHE 1;
ALTER TABLE public.user_id_seq OWNER TO postgres;
What I'm trying is in my table the column user_id should generate Auto from Base as 6000 and increment each time by 1.
Whenever I call API from my service it is creating new record. But the user_id is not showing anything.
I have three tables setup using PostgreSQL 9.4.5. Some details removed.
Table: component
id | bigint | not null default nextval('component_seq'::regclass) |
Table: file
id | bigint | not null default nextval('file_seq'::regclass) |
Table: component_file
id | bigint | not null default nextval('component_file_seq'::regclass) |
component_id | bigint | not null |
file_id | bigint | |
usage | text | not null |
Essentially, it's a many-to-many relationship with additional columns in the many-to-many join table.
A file can be associated to one or more components.
A component can be associated to one or more files.
It is possible for a component to be associated with no files which is why the component_file.file_id is nullable.
I have modeled this using JPA with Hibernate as my implementation provider. I use OneToMany associations (Component and File) in order to have access to the associated join table metadata and two ManyToOne associations for the join table object representation (ComponentFile)
public class Component {
...
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "component_seq")
#Column(name = "id", nullable = false, insertable = true, updateable = false)
private Long id;
#OneToMany(mappedBy = "component", cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.LAZY, orphanRemoval = true)
private List<ComponentFile> componentFiles;
...
}
public class File {
...
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "file_seq")
#Column(name = "id", nullable = false, insertable = true, updateable = false)
private Long id;
#OneToMany(mappedBy = "file", cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.LAZY, orphanRemoval = true)
private List<ComponentFile> componentFiles;
...
}
public class ComponentFile {
...
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "component_id", referencedColumnName = "id", nullable = false, insertable = true, updateable = false)
private Component component;
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "file_id", referencedColumnName = "id", nullable = true, insertable = true, updateable = false)
private File file;
...
}
All is working fine except I have indeterminite insert order.
If I insert a component without a file (1 component row and 1 component_file row), the persistence is fine.
If I insert a multiple components associated to a single file (1 component row, 2 file rows, 2 component_file rows), then an error occurs because Hibernate is inserting a component_file row with a null file_id reference. This causes a constraint violation due to a unique constraint as Hibernate is inserting two rows with the same component id and NULL file id which is not allowed (unique constraint on component_file.component_id where component_file.file_id IS NULL).
2016-01-26 10:59:30,506 ERROR [SqlExceptionHelper] - Batch entry 1 insert into component_file (usage, component_id, file_id, id) values ('INCLUDED', '180', NULL, '202') was aborted. Call getNextException to see the cause.
2016-01-26 10:59:30,506 WARN [SqlExceptionHelper] - SQL Error: 0, SQLState: 23505
2016-01-26 10:59:30,507 ERROR [SqlExceptionHelper] - ERROR: duplicate key value violates unique constraint "uidx_component_file_component_id" Detail: Key (component_id)=(180) already exists.
2016-01-26 10:59:30,509 ERROR [BatchingBatch] - HHH000315: Exception executing batch [could not execute batch]
2016-01-26 10:59:30,512 INFO [DbConstraintNameRetriever] - Constraint name retrieval results [Name: uidx_component_file_component_id | Original class: java.sql.BatchUpdateException | Message: ERROR: duplicate key value violates unique constraint "uidx_component_file_component_id" Detail: Key (component_id)=(180) already exists. | Postgres exception?: true | Batch update exception?: true].
Why is this occurring and what is the workaround or alternative methods for solving this type of relationship and persistence?
Try to add:
cascade = { CascadeType.PERSIST, CascadeType.MERGE }
for Component and File #ManyToOne annotations in ComponentFile.
Since you have specified mappedBy attribute in the OneToMany annotation, make sure that you are also setting the other end of the relationship by calling ComponentFile.setComponent(...) or ComponentFile.setFile(...) appropriately whenever you are adding ComponentFile to the arrayList in Component or File entity.
If you still see the same error or if you are already doing it, posting the Entity creation and association logic would help.
I'm new in Hibernate, and I've started to do a little project.
I wanted to test add and remove for all kind of relashionship.
I do not understant how remove is managed ... (for ManyToMany it seems to works well)
(I use an H2 database)
I have a ManyToOne relashionship :
WORK have 0 or many STEPS so : (java and sql at the end)
I have created some entities, and then I just want de remove a Work from database ...
Details
I have create a Junit to Test, and I display my database :
- save new work : "workOne"
- save new work : "workTwo"
- save new file : "fileone"
- save new step : "stepOne"
- stepOne.setWork(workOne)
- save new step : "stepTwo"
- stepOne.setWork(workOne)
- stepOne.setFile(fileOne)
To save :
entityManager.getTransaction().begin();
entityManager.save(object);
entityManager.getTransaction().commit();
Display (by sql request on database) >>> OK all my entity are displayed with the good associations :
workOne - ID = 10
workTwo - ID = 11
fileOne - ID = 20
stepOne - ID = 40 / ID_WORK = 10 / ID_DOS = null
stepTwo - ID = 41 / ID_WORK = 11 / ID_DOS = 20
Now I want to test remove a work (in the same test)
// Remove a work
entityManager.getTransaction().begin();
entityManager.remove(workOne);
entityManager.getTransaction().commit();
// Display BDD
>> The workOne has not been deleted from database ... (no hibernate delete on logs)
I Expected that :
workOne (#10) would be deleted
stepOne too ... (because we cannot have a step with no work)
Thanks,
java :
#Entity
#Table(name = "T_WORK")
public class Work {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id = 0L;
#Column(name = "LABEL")
String label;
#Entity
#Table(name = "T_STEP")
public class Step{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id = 0L;
#ManyToOne(cascade = {CascadeType.ALL })
#JoinColumn(name = "ID_WORK")
Work work;
#OneToOne(cascade = {CascadeType.ALL })
#JoinColumn(name = "ID_FILE")
File file;
#Column(name = "LABEL")
private String label;
#Column(name = "STATUS")
private String status;
h2.sql :
create table T_WORK (
ID number(10) not null,
LABEL varchar2(64) not null,
constraint PK_WORK primary key (ID)
);
create table T_STEP (
ID number(10) not null,
ID_WORK number(10) not null,
ID_FILE number(10),
LABEL varchar2(64) not null,
STATUS varchar2(64),
constraint PK_STEP primary key (ID),
CONSTRAINT FK_STEPS_WORK FOREIGN KEY (ID_TRA) REFERENCES T_WORK(ID),
CONSTRAINT FK_STEP_FILE FOREIGN KEY (ID_DOS) REFERENCES T_FILE(ID)
);
Hibernate is generating invalid SQL for a particular criteria query. I can manually fix the query by adding single quotes to the value being used in the WHERE clause.
To fix it, I changed the query from:
where (role0_.ROLE_ID=2L )
to:
where (role0_.ROLE_ID=`2L` )
How to force hibernate to add single quotes (in mysql it is single quotes but in other database systems it might be something else) to enclose the values used in generated SQL queries?
The full generated query is:
select permission1_.PERMISSION_ID as PERMISSION1_12_,
permission1_.IS_REQUIRED as IS2_12_,
permission1_.SOURCE_ROLE_ID as SOURCE3_12_,
permission1_.TARGET_ROLE_ID as TARGET4_12_
from (
select ROLE_ID,
NAME,
DESCRIPTION,
IS_ACTION,
LABEL,
null as FIRST_NAME,
null as LAST_NAME,
null as PASSWORD_HASH,
1 as clazz_ from GROUPS
union
select ROLE_ID,
NAME,
null as DESCRIPTION,
null as IS_ACTION,
null as LABEL,
FIRST_NAME,
LAST_NAME,
PASSWORD_HASH,
2 as clazz_ from USERS
)
role0_ inner join PERMISSIONS permission1_ on role0_.ROLE_ID=permission1_.SOURCE_ROLE_ID
where (role0_.ROLE_ID=2L )
Basically I'd like this single quotes to be added by Hibernate.
The criteria query that generated this query is:
EntityManager entityManager = getEntityManager();
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object> criteriaQuery = criteriaBuilder.createQuery();
Class<?> queryScopeClass = temp.pack.commons.user.Role.class;
Root<?> from = criteriaQuery.from(queryScopeClass);
Path<?> idAttrPath = from.get("id");
// also tried criteriaBuilder.equal(idAttrPath, new Long(2))
Predicate predicate = criteriaBuilder.equal(idAttrPath, criteriaBuilder.literal(new Long(2)))
criteriaQuery.where(predicate);
Path<?> attributePath = from.get("permissions");
PluralAttributePath<?> pluralAttrPath = (PluralAttributePath<?>)attributePath;
PluralAttribute<?, ?, ?> pluralAttr = pluralAttrPath.getAttribute();
Join<?, ?> join = from.join((SetAttribute<Object,?>)pluralAttr);
TypedQuery<Object> typedQuery = entityManager.createQuery(criteriaQuery.select(join));
return (List<P>)typedQuery.getResultList();
Please let me know if you have any clues on how to force Hibernate to add those single quotes to the values (not the column/table name).
In my entity Role, the id property that appears in the WHERE clause is of long type, of course.
Follow up: The type of the id column in the database is bingint:
+---------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+-------+
| ROLE_ID | bigint(20) | NO | PRI | NULL | |
...
This is how the Role class has been annotated:
#Entity(name="Role")
#Table(name = "ROLES")
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#javax.persistence.TableGenerator(
name="GENERATED_IDS",
table="GENERATED_IDS",
valueColumnName = "ID"
)
public abstract class Role implements Serializable {
private static final long serialVersionUID = 1L;
/**
* The id of this role. Internal use only.
*
* #since 1.0
*/
#Id #GeneratedValue(strategy = GenerationType.TABLE, generator="GENERATED_IDS")
#Column(name = "ROLE_ID")
protected long id;
/**
* Set of permissions granted to this role.
*
* #since 1.0
*/
#OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, mappedBy="sourceRole")
protected Set<Permission> permissions = new HashSet<Permission>();
...
}
I use table per class inheritance strategy, that's why you see the union in the generated query for User and Group entities. They extend Role. Id is defined in Role.
Thank you!
Eduardo
The hibernate property hibernate.globally_quoted_identifiers=true will do the trick
Change your id to the Long class type instead of a primitive. Hibernate will then simply generate the query to be ROLE_ID=2, which is 100% valid since numbers don't require ticks or quotes.