i need your help,
i'm using a chained transaction for insering in two database simultanasly, the problem i have is my entity is created with tableGenerator from my id and when i'm using chained transaction it goes to my first database searching for this table and throws table doesn't exist because my table is in the second database not the first. i was hoping that chained transaction is intelligent enough to know which database search for this table but its not the case. can someone please help how can i acheive that ?
#Entity
public class Group {
#Id
#Column(name = "groupId", updatable = false, insertable = false)
#GeneratedValue(strategy = GenerationType.TABLE, generator = "counter")
#TableGenerator(name = "counter",
table = "Counter",
valueColumnName = "currentId",
pkColumnName = "name",
pkColumnValue = "com.model.Counter",
allocationSize = 1)
private Long groupId;
}
#Override
public void createGroup(Group group) {
create(group);
}
#Transactional(value = "chainedDatabaseManager")
public void creerLiferyGroup(Group group, User user){
//database 1 insert
UserService.createUser(user);
//database2 insert
groupService.createGroup(group);
}
The exception is Table on database1.Counter doesn't exist which is normal because my Table Counter is in my second database with the Group entity
Related
I have #NamedQuery. I want to add a lot of different filters to the query as per condition at runtime. There is another concept of #Filter in Hibernate. can this concept be a merge to have a combined result?
suppose I have
#NamedQuery(name="Users.someUsers",query="select u from Users where firstname='bob'")
suppose I want to filter the result according to some other parameter.
can I add #Filter that can do the trick?
supposed I want to an add age filer or a place filter over the existing Users.someUsers by enabling the corresponding filter on the underlying hibernate session?
I suppose you want to define named queries and filters at entity level and expect named queries to have filters which you defined.
I wrote a test for it:
#Entity
#Table(name = "DEPARTMENT")
#NamedQueries({#NamedQuery(name=DepartmentEntity.GET_DEPARTMENT_BY_ID, query=DepartmentEntity.GET_DEPARTMENT_BY_ID_QUERY),})
#FilterDef(name="deptFilter", parameters={#ParamDef( name="name", type="string")})
#Filters( {#Filter(name="deptFilter", condition=":name = name")})
public class DepartmentEntity implements Serializable {
static final String GET_DEPARTMENT_BY_ID_QUERY = "from DepartmentEntity d where d.id = :id";
public static final String GET_DEPARTMENT_BY_ID = "GET_DEPARTMENT_BY_ID";
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
private Integer id;
#Column(name = "NAME", unique = true, nullable = false, length = 100)
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Now you can use both like this:
Session session = (Session) entityManager.getDelegate();
Filter filter = session.enableFilter("deptFilter");
filter.setParameter("name", name);
return (DepartmentEntity) session.getNamedQuery(DepartmentEntity.GET_DEPARTMENT_BY_ID)
.setParameter("id", id)
.uniqueResult();
Query generated by hibernate:
select department0_.ID as ID1_3_, department0_.NAME as NAME2_3_ from DEPARTMENT department0_ where ? = department0_.name and department0_.ID=?
You will need to add filters to session and then create named query. If this doesn't cover your use case then post example exactly what you want to acheive.
This is what CriteriaQuery is built for. It allows you to create queries at runtime.
Avoid using named queries for queries which you want to build at runtime based on user input.
Example
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<EntityType> criteria = builder.createQuery(EntityType.class);
Root<EntityType> root = criteria.from(EntityType.class);
criteria.where(
builder.equal(root.get("owner"), "something")
);
// Any conditions can be added to criteria here ar runtime based on user input
List<Topic> topics = entityManager
.createQuery(criteria)
.getResultList();
Named queries are precompiled at EntityManagerFactory startup, so they add performance benefits as long as queries are static, but for dynamic queries consider using CriteriaQueries
I am running the latest version of Hibernate with 2 entities: Project and ProjectShare, which have a one-to-many relation set up bidirectional.
A ProjectShare is uniquely identified by a composite ID containing project_id and user_id. Besides the key, a ProjectShare contains a boolean flag whether the user gets read or write access on the project.
#Entity
#Table(name = "projects")
public class Project {
#Id
#GeneratedValue // UUID generator
#Column(name = "project_id")
private String id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "project", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ProjectShare> shares = new ArrayList<>();
public Project(String name) {
this.name = name;
}
public void addShare(ProjectShare share) {
shares.add(share);
share.setProject(this);
}
public void removeShare(ProjectShare share) {
shares.remove(share);
share.setProject(null);
}
}
#Entity
#Table(name = "project_shares")
public class ProjectShare {
#EmbeddedId
private ProjectShareId id;
#Column(name = "has_write_access")
private boolean hasWriteAccess;
#MapsId("projectId")
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "project_id", nullable = false, updatable = false)
private Project project;
public ProjectShare(ProjectShareId id, boolean hasWriteAccess) {
this.id = id;
this.hasWriteAccess = hasWriteAccess;
}
public void setProject(Project project) {
this.project = project;
}
}
#Embeddable
public class ProjectShareId implements Serializable {
#Column(name = "project_id")
private String projectId;
#Column(name = "user_id")
private String userId;
public ProjectShareId(String userId) {
this.userId = userId;
}
// equals and hashCode go here...
}
If I create a new Project and assign new ProjectShare associations to it, everything works fine:
Project project = new Project("my_project");
project.addShare(new ProjectShare(new ProjectShareId("user1"), false));
project.addShare(new ProjectShare(new ProjectShareId("user2"), false));
projectRepository.save(project); // assume project id is '123'
Since all objects are new and not yet persisted, it executes 1 insert for the Project itself and 1 insert for each ProjectShare. Lets assume the project is inserted with id '123'.
Now, if I load this existing project, and add new ProjectShares to it, things go wrong:
Project project = projectRepository.findById("123");
project.addShare(new ProjectShare(new ProjectShareId("user2"), true));
project.addShare(new ProjectShare(new ProjectShareId("user3"), true));
projectRepository.save(project);
For every ProjectShare, this executes a SELECT on the values in ProjectShareId (project_id and user_id), followed by either an INSERT of UPDATE, depending on whether the record was found. This is the basic merge strategy of Hibernate and this is what we want.
The desired outcome of this should be:
Leave ProjectShare for user1 untouched
Update the ProjectShare for user2 (from false to true)
Create a new ProjectShare for user3
However, when the SELECT is executed for the ProjectShare, the foreign key project_id is always null. This means that existing records are never found, an INSERT is attempted instead of and UPDATE, and a DB-level Constraint violation is triggered.
How should I solve this issue? Should I manually go through the project.getShares() collection, find existing records and update them instead? I was hoping Hibernate would do this through its merge strategy.
Or could this be a bug in Hibernate related to associations with foreign keys in Embbedded IDs?
My entities are as follows
#Entity
#Table(
name = "t1",
uniqueConstraints = {
#UniqueConstraint(name = "u1", columnNames = {"u1"}),
#UniqueConstraint(name = "u2", columnNames = {"u2_id"}) //This does not enforce uniqueness
}
)
public class t1
{
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
private String u1; //Uniqueness is enforced here
#OneToOne
#JoinColumn(unique = true) //This does not enforce uniqueness
private U2 u2;
}
#Entity
#Table(
name = "u2",
uniqueConstraints = {
#UniqueConstraint(name = "p1", columnNames = {"p1"})
}
)
public class u2
{
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
private String p1; //Uniqueness is enforced here
}
Creating and updating the entities by
t1 t1_0 = new t1("t1_0");
t1 t1_1 = new t1("t1_1");
u2 u2_0 = new u2("u2_0");
repository.save(t1_0);
repository.save(t1_1);
repository.save(u2_0);
t1_0 = repository.findOne(t1_0.getId());
t1_1 = repository.findOne(t1_1.getId());
t1_0.setu2(u2_0);
t1_1.setu2(u2_0);
After the transaction finishes t1_0.u2 is tied to u2_0 and t1_1.u2 is tied to u2_0. I expected that it would throw uniqueness constraint violation exception.
Do not understand what is wrong. Other threads on SO suggest that #JoinColumn(unique = true) should do it.
EDIT
This is wrapped in one transaction
t1_0 = repository.findOne(t1_0.getId());
t1_1 = repository.findOne(t1_1.getId());
t1_0.setu2(u2_0);
t1_1.setu2(u2_0);
EDIT2
For some reason I can't access the database either. The console view loads but after adding password and user blank page is shown (some network queries fail).
But I can see from log that foreign keys are created and unique constraint aswell
Hibernate: alter table driver add constraint UK_ssh305wwvomjtn6opolug33nj unique (cardo_id)
Hibernate: alter table car add constraint FKosnia01vhqwmm888uxrg4o6f6 foreign key (manufacturerdo_id) references manufacturer
Hibernate: alter table driver add constraint FK3yb5ci9sr6ieo6n4wwwef3puv foreign key (cardo_id) references car
When adding
My transactions were invalid. As they do not see each others data, I mistakenly created one within another.
Columns names (if more than one) should be introduced with commas.
Your convention is wrong
Disclaimer: not checked on real database
#Table(
name = "t1",
uniqueConstraints = {
#UniqueConstraint(name = "u1", columnNames = {"u1"}),
#UniqueConstraint(name = "u2", columnNames = {"u2", "id"}) // comma
}
)
I am working on a Spring-MVC application where I have a many-to-many relationship in which I have to query in 2 tables to get the values I require. I will explain in more detail.
I have 2 tables GroupAccount, GroupMembers with many-to-many
relationship. Now there is a junction table called membertable where
id from GroupMembers and GroupAccount is stored.
This is what I am looking for :
I pass a groupAccounId and username as parameters. Now, in the
GroupMembers table, there is a username stored. In groupAccount,
there is groupAccountId is stored.
Now in the memberjunction, I have composite key
memberid,GroupAccountId, I would like the member id for the username
which has a matching groupAccountId I submit.
Below is the SQL code and Spring-mvc code to understand more better.
CREATE TABLE public.groupaccount (
groupid NUMERIC NOT NULL,
groupname VARCHAR,
groupaccountstatus BOOLEAN DEFAULT false NOT NULL,
adminusername VARCHAR,
CONSTRAINT groupid PRIMARY KEY (groupid)
);
CREATE TABLE public.groupmembers (
memberid INTEGER NOT NULL,
musername VARCHAR
CONSTRAINT memberid PRIMARY KEY (memberid)
);
CREATE TABLE public.memberjunction (
memberid INTEGER NOT NULL,
groupid NUMERIC NOT NULL,
CONSTRAINT membergroupid PRIMARY KEY (memberid, groupid)
);
GroupMembersDAOImpl :#
#Override
public List<Integer> returnMemberIdWithMatchingUsername(String memberUsername) {
session = this.sessionFactory.getCurrentSession();
org.hibernate.Query query = session.createQuery("From GroupMembers as " +
"n where n.memberUsername=:memberUsername");
query.setParameter("memberUsername",memberUsername);
List<GroupMembers> memberList = query.list();
List<Integer> memberIdList = new ArrayList<>();
for(GroupMembers members :memberList){
memberIdList.add(members.getMemberid());
}
return memberIdList;
}
GroupAccount model :
#Entity
#Table(name="groupaccount")
public class GroupAccount {
#Id
#Column(name="groupid")
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "groupaccount_seq_gen")
#SequenceGenerator(name = "groupaccount_seq_gen",sequenceName = "groupaccount_seq")
private Long groupId;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "memberjunction", joinColumns = {#JoinColumn(name = "groupid")},
inverseJoinColumns = {#JoinColumn(name = "memberid")})
private Set<GroupMembers> groupMembersSet = new HashSet<>();
public void setGroupMembersSet(Set<GroupMembers> groupMembersSet){
this.groupMembersSet = groupMembersSet;
}
}
GroupMembers model class :
#Entity
#Table(name="groupmembers")
public class GroupMembers {
#Id
#Column(name="memberid")
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "groupmembers_seq_gen")
#SequenceGenerator(name = "groupmembers_seq_gen",sequenceName = "groupmembers_seq")
private int memberid;
#ManyToMany(mappedBy = "groupMembersSet")
private Set<GroupAccount> groupAccounts = new HashSet<>();
public void setGroupAccounts(Set<GroupAccount> groupAccounts){
this.groupAccounts = groupAccounts;
}
public Set<GroupAccount> getGroupAccounts(){
return this.groupAccounts;
}
}
Query I am using :
#Override
public int getMemberIdForCanvas(String memberUsername, Long groupId) {
session = this.sessionFactory.getCurrentSession();
org.hibernate.Query query = session.createQuery("select distinct m.memberId from GroupMembers m\n" +
"join m.groupAccounts a\n" +
"where a.memberUsername = :userName and m.groupId=:groupId");
query.setParameter(memberUsername,"memberUsername");
query.setParameter(String.valueOf(groupId),"groupId");
int memberid = (Integer)query.uniqueResult();
return memberid;
}
Any help would be nice. Thanks a lot.
Here's the documentation for joins and HQL. Please read it.
The query is as simple as
select distinct m.memberId from GroupMembers m
join m.groupAccounts a
where a.memberUsername = :userName
Please also fix your naming. A GroupMembers instance is a single group member. So the class should be named GroupMember, without s. Repeating the name of the class in the fields of this class is also redundant: member.getId() is more readable and less verbose than member.getMemberId(). Same for the other fields.
In the code below, when I call the method EntityADAO.findByGroupId(...), the table EntityA is locked until the transaction is complete. I don't want this. How can I avoid the table locking? Thanks in advance.
My database is SQL SERVER 2012. I am suing Hibernate 4.0.2.
Below is the code excerpt:
#Entity
#Table(name = EntityA)
#NamedQueries ({
#NamedQuery(name="EntityA.findByGroupId", query="SELECT p FROM EntityA p WHERE p.groupId= :groupId")})
public class EntityA implements Serializable {
#Id
#Column(name = "EntityKey", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long entityKey;
#Version
#Column(name = "Version", nullable = false)
private Long version = -1l;
#NotNull
#Column(name = "GroupId")
private Integer groupId;
}
#Repository("EntityA")
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class EntityADAO extends AbstractJpaDAO<EntityA> {
#PersistenceContext
protected EntityManager em;
public EntityADAO() {
setClazz(EntityADAO.class);
}
**//A call to this method locks the table EntityA until the transaction is complete**
public List<EntityA> findByGroupId(int groupId) {
TypedQuery<EntityA> query = em.createNamedQuery("EntityA.findByGroupId", EntityA.class);
query.setParameter("groupId", groupId);
return query.getResultList();
}
}
If I'm not wrong SQL Server does not come with row versioning enabled by default, hence I think this is why you're seeing this behavior.
What I suspect is when you issue a select query, and before this query finishes you issue another update query, the update has to wait until the select is complete.
There are many approach to solve this problem, with one being enabling the row versioning. Other option include using the least restrictive isolation level.