Сначала я кратко расскажу о своей проблеме.
Я работаю над пилотным проектом, чтобы проверить то, что я недавно узнал в Hibernate. Идея состоит в том, чтобы интегрировать Spring, Spring MVC и Hibernate. Кажется, все работало нормально, пока я не столкнулся с этой проблемой, которая заняла у меня целый день, но все еще оставила меня без решения.
Идея довольно проста. Есть две сущности: «роль» и «пользователь». У человека может быть одна роль, что делает его отношениями «многие к одному». Роль может быть назначена множеству людей, что означает отношения «один ко многим».
Поэтому мой код такой.
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
@Column(length=20,nullable=false,name = "rolename",unique=true)
private String name;
@OneToMany(cascade=CascadeType.ALL, mappedBy = "role")
private Set<UserBean> users = new HashSet<UserBean>();
Пользователь:
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@ManyToOne(optional=true,cascade=CascadeType.ALL,unique=true)
@JoinColumn(name = "role_id")
private RoleBean role=new RoleBean();
Кроме того, я использовал Spring MVC для привязки значения. Вот код :
<form:form modelAttribute = "user"
action = "${pageContext.request.contextPath}/add">
<div>
<form:input path = "username"
/>
<form:errors path = "username" />
<br>
<form:password path = "password" placeholder = "password" />
<form:errors path = "password" />
<br> <form:select path = "role.name">
<option value = "clerk">clerk</option>
<option value = "salesperson">salesperson</option>
<option value = "manager">manager</option>
</form:select>
</div>
<input type = "submit" value = "submit" />
</form:form>
Вы увидите, что на интерфейсной странице я добавил тег <form:select> в надежде, что этот тег будет работать с настройкой «каскад» для выполнения работы.
Это сработало просто отлично.
Однако по мере того, как я тестировал код дальше, кое-что произошло.
Я ожидал, что в идеале Hibernate получит «имя роли», затем создаст элемент роли, используя это имя, и сохранит эту новую роль в базе данных. Если два или более пользователей регистрируются с одним и тем же «именем роли» (которым в этом параметре является клерк, менеджер или продавец), спящий режим может понять, что не предполагается снова вставлять значение в таблицу «роли» из-за настройки unique=true. .
Затем произошло нечто странное. Если я дважды зарегистрируюсь с двумя «пользователями», использующими одно и то же «имя роли», спящий режим выдаст мне сообщение об ошибке:
Duplicate entry 'clerk' for key 'UK_a5k2ae0srn5n63xyhst1dllhq'
Честно говоря, я знаю, как произошла эта ошибка. Произошло это из-за настройки "unique=true". Однако, если я удалю этот параметр и снова начну программирование, программа может автоматически вставить в систему несколько «ролевых» элементов с одинаковым именем роли, так что таблица может выглядеть примерно так:
Role:
id name
1 clerk
2 clerk
3 clerk
4 clerk
.........
По-видимому, это не то, что я хотел, потому что никто не ожидал, что компания / организация может иметь разные роли с одним и тем же именем.
Я полагаю, что должно быть что-то, что я сделал не так, либо в настройке отображения гибернации, либо в каскаде. Но хотя я очень старался найти возможный ответ как здесь, так и через Google, я не нашел ничего, что решало бы эту проблему.
Итак, не могли бы вы дать мне возможное решение?
Загляните в свой код, когда вы сохраняете пользователя ... вам не нужно создавать новую роль, просто назначьте ее пользователю.
Хорошо, вот автоматически сгенерированный оператор sql: Hibernate: вставить в значения t_roles (имя, идентификатор) (?,?)
Последовал твоему совету, поменяв избавление от "нового", все равно не работает ..... Все же! Огромное спасибо!
вы можете опубликовать свой файл сохранения?
ОК! Я пишу сейчас! Заранее спасибо!
защищенный сеанс getSession () {return sessionFactory.getCurrentSession (); } общедоступное сохранение T (запись T) {this.getSession (). saveOrUpdate (запись); возвратная запись; }
Значение CascadeType.ALL заключается в том, что постоянство будет распространять (каскадировать) все операции EntityManager (PERSIST, REMOVE, REFRESH, MERGE, DETACH) на связанные объекты.
Поскольку вам не нужно создавать новую роль каждый раз, когда вы сохраняете пользователя, просто удалите его из роли.
@OneToMany(mappedBy = "role")
private Set<UserBean> users;
Добавьте сеттер и получатель. . .
То же самое в User.
Я пробовал это решение раньше ... Проблема в том, что если я удалил "каскад", спящий режим выдает другое исключение, которое говорит примерно так: "переходное значение ......", что в основном означает, что я должен сохранить временная сущность (которая в данном случае является "ролью") до того, как я сохраню пользователя ..... Плюс, у меня есть установщик и получатель ... ... Спасибо!
Без настойчивости трудно понять проблему
Ну ... Я не уверен, что такое "настойчивость" ...?
Вам следует изменить несколько вещей:
rolesList является атрибутом modelAttribute, который содержит все возможные роли):<form:select path = "role.id">
<form:options items = "${rolesList}" itemValue = "id" itemLabel = "name"/>
</form:select>
UserBean с RoleBean, установленным для выбранной роли во внешнем интерфейсе, а Hibernate позаботится о сохранении role_id.Спасибо! Это кажется правильным ответом, потому что я действительно думал, что это может быть правильным путем. Однако я не понимаю, стоит ли мне добавить два атрибута modelAttributes в тег <form: form>? Один из них - «пользовательский бин», а другой - «ролевой»?
Кроме того, могу я спросить, как я могу передать список «ролей» во внешний интерфейс таким образом, чтобы Hibernate знал, что такое каждый элемент, когда значение возвращается обратно в серверную часть?
Атрибут itemValue определяет поле, которое определяет выбранную роль в серверной части. Используйте первичный ключ объекта как itemValue. Этого должно хватить.
Во-первых, я хочу поблагодарить вас за ответы на мои вопросы и предложения решений!
Наконец, я выполнил эту работу, следуя советам @Mubin.
Я добавил несколько кодов в контроллер:
ArrayList<RoleBean> rolelist = new ArrayList<RoleBean>();
RoleBean r1 = new RoleBean();
RoleBean r2 = new RoleBean();
RoleBean r3 = new RoleBean();
r1.setId(1);
r1.setName("clerk");
r2.setId(2);
r2.setName("salesman");
r3.setId(3);
r3.setName("manager");
rolelist.add(r1);
rolelist.add(r2);
rolelist.add(r3);
view.addObject("rolelist", rolelist);
Итак, вкратце подведу итоги для вашей справки!
Во-первых, моя первоначальная идея неверна. Я подумал, что мне нужно передать «роли» из внешнего интерфейса в серверный. Однако эта идея сама по себе противоречит логике. Компании создают роли и назначают эти роли разным сотрудникам. Редко будет сценарий, когда сотрудники требуют, чтобы компании создавали для них роли.
Итак, во-первых, я должен сначала создать роли в базе данных с помощью SQL.
После того, как я понял это, все казалось гладким. Если роли в базе данных остаются постоянными, все, что мне нужно сделать, это передать информацию внешнему ключу в таблице пользователей, связав каждого человека с его ролью.
Итак, я использовал этот тег:
<form:select path = "role.id">
<form:options items = "${rolelist}" itemValue = "id" itemLabel = "name"/>
</form:select>
Что делает спящий режим:
он принимает входные данные от внешнего интерфейса, проверьте "itemValue", то есть user.role.id. Затем сравните, есть ли это значение уже в базе данных (что абсолютно «да»), затем свяжите идентификатор с ролью пользователя.
Я думаю, что это очень важно, потому что вчера, когда я пытался решить проблему, была попытка, когда я просто удалил cascade = ALL для rolebean, а затем программа выдала исключение: временные объекты не сохранены.
Это произошло потому, что без каскада программа просто сохранит «пользователя» без предварительного сохранения «роли». Если в таблице нет «роли», значит, нет «идентификатора роли» для присоединения.
Итак, проблема решена! Спасибо!
Можете ли вы показать сценарий создания ваших таблиц?