Если данные поступают в файл, могут быть реализованы таким образом:
На Scala можно реализовать таким образом:
val df = spark.read.option("header", "false").csv("non-csv.txt")
val remainingColumns = df.columns.tail
df.withColumn("id", monotonically_increasing_id).
select(
col("id"),
col(df.columns(0)),
array(remainingColumns.head, remainingColumns.tail: _*)
).show(false)
Вывод:
+---+---+--------------------+
|id |_c0|array(_c1, _c2, _c3)|
+---+---+--------------------+
|0 |abc|[ x1, x2, x3] |
|1 |def|[ x1, x3, x4] |
|2 |ghi|[ x7, x10, x11] |
+---+---+--------------------+
Итак, я наконец нашел рабочее решение:
Отключить EntityManager для обновления DTYPE . Это главным образом потому, что Query.executeUpdate () должен выполняться внутри транзакции. Вы можете попробовать запустить его в рамках существующей транзакции, но это, вероятно, связано с тем же контекстом сохраняемости сущности, которую вы изменяете. Это означает, что после обновления DTYPE вы должны найти способ evict () сущности. Самый простой способ - вызвать entityManager.clear () , но это приводит к всевозможным побочным эффектам (об этом читайте в спецификации JPA). Лучшее решение - получить базовый делегат (в моем случае Hibernate Session ) и вызовите Session.evict (пользователь) . Это, вероятно, будет работать на простых доменных графах, но мои были очень сложными. Мне никогда не удавалось заставить @Cascade (CascadeType.EVICT) корректно работать с моими существующими аннотациями JPA, например @OneToOne (cascade = CascadeType.ALL) . Я также попытался вручную передать свой домен графа Session и заставить каждую родительскую сущность выселить своих детей. Это также не сработало по неизвестным причинам.
Я остался в ситуации, когда будет работать только entityManager.clear () , но я не мог принять побочные эффекты. Затем я попытался создать отдельный блок персистентности специально для конвертации сущностей. Я подумал, что могу локализовать операцию clear () только для того ПК, который отвечает за преобразования. Я установил новый компьютер, новый соответствующий EntityManagerFactory , новый менеджер транзакций для него, и вручную добавление этого менеджера транзакций в репозиторий для ручной упаковки executeUpdate () в транзакцию, соответствующую соответствующему ПК. Здесь я должен сказать, что я недостаточно знаю о транзакциях, управляемых контейнером Spring / JPA, потому что это оказалось кошмаром, пытаясь заставить локальную / ручную транзакцию для executeUpdate () хорошо играть с Управляемая контейнером транзакция извлекается из уровня Service.
В этот момент я выбросил все и создал этот класс:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class JdbcUserConversionRepository implements UserConversionRepository {
@Resource
private UserService userService;
private JdbcTemplate jdbcTemplate;
@Override
@SuppressWarnings("unchecked")
public User convertUserType(final User user, final Class targetClass) {
// Update the DTYPE
jdbcTemplate.update("update user set user.DTYPE = ? where user.id = ?", new Object[] { targetClass.getSimpleName(), user.getId() });
// Before we try to load our converted User back into the Persistence
// Context, we need to remove them from the PC so the EntityManager
// doesn't try to load the cached one in the PC. Keep in mind that all
// of the child Entities of this User will remain in the PC. This would
// normally cause a problem when the PC is flushed, throwing a detached
// entity exception. In this specific case, we return a new User
// reference which replaces the old one. This means if we just evict the
// User, then remove all references to it, the PC will not be able to
// drill down into the children and try to persist them.
userService.evictUser(user);
// Reload the converted User into the Persistence Context
return userService.getUserById(user.getId());
}
public void setDataSource(final DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
Есть две важные части этого метода, которые, я считаю, заставляют его работать:
В этот момент я выбросил все и создал этот класс:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class JdbcUserConversionRepository implements UserConversionRepository {
@Resource
private UserService userService;
private JdbcTemplate jdbcTemplate;
@Override
@SuppressWarnings("unchecked")
public User convertUserType(final User user, final Class targetClass) {
// Update the DTYPE
jdbcTemplate.update("update user set user.DTYPE = ? where user.id = ?", new Object[] { targetClass.getSimpleName(), user.getId() });
// Before we try to load our converted User back into the Persistence
// Context, we need to remove them from the PC so the EntityManager
// doesn't try to load the cached one in the PC. Keep in mind that all
// of the child Entities of this User will remain in the PC. This would
// normally cause a problem when the PC is flushed, throwing a detached
// entity exception. In this specific case, we return a new User
// reference which replaces the old one. This means if we just evict the
// User, then remove all references to it, the PC will not be able to
// drill down into the children and try to persist them.
userService.evictUser(user);
// Reload the converted User into the Persistence Context
return userService.getUserById(user.getId());
}
public void setDataSource(final DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
Есть две важные части этого метода, которые, я считаю, заставляют его работать:
В этот момент я выбросил все и создал этот класс:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class JdbcUserConversionRepository implements UserConversionRepository {
@Resource
private UserService userService;
private JdbcTemplate jdbcTemplate;
@Override
@SuppressWarnings("unchecked")
public User convertUserType(final User user, final Class targetClass) {
// Update the DTYPE
jdbcTemplate.update("update user set user.DTYPE = ? where user.id = ?", new Object[] { targetClass.getSimpleName(), user.getId() });
// Before we try to load our converted User back into the Persistence
// Context, we need to remove them from the PC so the EntityManager
// doesn't try to load the cached one in the PC. Keep in mind that all
// of the child Entities of this User will remain in the PC. This would
// normally cause a problem when the PC is flushed, throwing a detached
// entity exception. In this specific case, we return a new User
// reference which replaces the old one. This means if we just evict the
// User, then remove all references to it, the PC will not be able to
// drill down into the children and try to persist them.
userService.evictUser(user);
// Reload the converted User into the Persistence Context
return userService.getUserById(user.getId());
}
public void setDataSource(final DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
Есть две важные части этого метода, которые, я считаю, заставляют его работать:
В этот момент я выбросил все и создал этот класс:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class JdbcUserConversionRepository implements UserConversionRepository {
@Resource
private UserService userService;
private JdbcTemplate jdbcTemplate;
@Override
@SuppressWarnings("unchecked")
public User convertUserType(final User user, final Class targetClass) {
// Update the DTYPE
jdbcTemplate.update("update user set user.DTYPE = ? where user.id = ?", new Object[] { targetClass.getSimpleName(), user.getId() });
// Before we try to load our converted User back into the Persistence
// Context, we need to remove them from the PC so the EntityManager
// doesn't try to load the cached one in the PC. Keep in mind that all
// of the child Entities of this User will remain in the PC. This would
// normally cause a problem when the PC is flushed, throwing a detached
// entity exception. In this specific case, we return a new User
// reference which replaces the old one. This means if we just evict the
// User, then remove all references to it, the PC will not be able to
// drill down into the children and try to persist them.
userService.evictUser(user);
// Reload the converted User into the Persistence Context
return userService.getUserById(user.getId());
}
public void setDataSource(final DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
Есть две важные части этого метода, которые, я считаю, заставляют его работать:
В этот момент я выбросил все и создал этот класс:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class JdbcUserConversionRepository implements UserConversionRepository {
@Resource
private UserService userService;
private JdbcTemplate jdbcTemplate;
@Override
@SuppressWarnings("unchecked")
public User convertUserType(final User user, final Class targetClass) {
// Update the DTYPE
jdbcTemplate.update("update user set user.DTYPE = ? where user.id = ?", new Object[] { targetClass.getSimpleName(), user.getId() });
// Before we try to load our converted User back into the Persistence
// Context, we need to remove them from the PC so the EntityManager
// doesn't try to load the cached one in the PC. Keep in mind that all
// of the child Entities of this User will remain in the PC. This would
// normally cause a problem when the PC is flushed, throwing a detached
// entity exception. In this specific case, we return a new User
// reference which replaces the old one. This means if we just evict the
// User, then remove all references to it, the PC will not be able to
// drill down into the children and try to persist them.
userService.evictUser(user);
// Reload the converted User into the Persistence Context
return userService.getUserById(user.getId());
}
public void setDataSource(final DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
Есть две важные части этого метода, которые, я считаю, заставляют его работать:
В этот момент я выбросил все и создал этот класс:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class JdbcUserConversionRepository implements UserConversionRepository {
@Resource
private UserService userService;
private JdbcTemplate jdbcTemplate;
@Override
@SuppressWarnings("unchecked")
public User convertUserType(final User user, final Class targetClass) {
// Update the DTYPE
jdbcTemplate.update("update user set user.DTYPE = ? where user.id = ?", new Object[] { targetClass.getSimpleName(), user.getId() });
// Before we try to load our converted User back into the Persistence
// Context, we need to remove them from the PC so the EntityManager
// doesn't try to load the cached one in the PC. Keep in mind that all
// of the child Entities of this User will remain in the PC. This would
// normally cause a problem when the PC is flushed, throwing a detached
// entity exception. In this specific case, we return a new User
// reference which replaces the old one. This means if we just evict the
// User, then remove all references to it, the PC will not be able to
// drill down into the children and try to persist them.
userService.evictUser(user);
// Reload the converted User into the Persistence Context
return userService.getUserById(user.getId());
}
public void setDataSource(final DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
Есть две важные части этого метода, которые, я считаю, заставляют его работать:
В этот момент я выбросил все и создал этот класс:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class JdbcUserConversionRepository implements UserConversionRepository {
@Resource
private UserService userService;
private JdbcTemplate jdbcTemplate;
@Override
@SuppressWarnings("unchecked")
public User convertUserType(final User user, final Class targetClass) {
// Update the DTYPE
jdbcTemplate.update("update user set user.DTYPE = ? where user.id = ?", new Object[] { targetClass.getSimpleName(), user.getId() });
// Before we try to load our converted User back into the Persistence
// Context, we need to remove them from the PC so the EntityManager
// doesn't try to load the cached one in the PC. Keep in mind that all
// of the child Entities of this User will remain in the PC. This would
// normally cause a problem when the PC is flushed, throwing a detached
// entity exception. In this specific case, we return a new User
// reference which replaces the old one. This means if we just evict the
// User, then remove all references to it, the PC will not be able to
// drill down into the children and try to persist them.
userService.evictUser(user);
// Reload the converted User into the Persistence Context
return userService.getUserById(user.getId());
}
public void setDataSource(final DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
Есть две важные части этого метода, которые, я считаю, заставляют его работать:
Хотя мои первоначальные тесты прошли успешно, у этого решения все еще могут быть некоторые проблемы. Я буду держать это в курсе, так как они раскрыты.
Вы действительно должны представлять уровень подписки в типе User? Почему бы вам не добавить тип подписки в вашей системе?
Отношение тогда будет выражаться как: у пользователя есть одна подписка. Это также может быть смоделировано как отношение «один ко многим», если вы хотите сохранить историю подписок.
Затем, когда вы захотите изменить уровень подписки пользователя вместо создания нового пользователя, вы создадите нового Подписка и назначение ее пользователю.
// When you user signs up
User.setSubscription(new FreeSubscription());
// When he upgrades to a paying account
User.setSubscription(new PayingSubscription());
Действительно ли TrialUser отличается от PayingUser? Возможно нет. Вам лучше будет использовать агрегацию, а не наследование.
Данные подписки могут храниться в отдельной таблице (сопоставленной как сущность) или в таблице пользователей (сопоставленной как компонент).
Проблема больше связана с Java, чем с чем-либо еще. Вы не можете изменить тип экземпляра во время выполнения (во время выполнения), поэтому hibernate не предусматривает такого рода сценариев (в отличие, например, от rails).
Если вы хотите исключить пользователя из сеанса, вы будете должны выселить связанные лица. У вас есть несколько вариантов:
У меня была похожая проблема в grails, и мое решение аналогично решению Григория . Я копирую все поля экземпляра, включая связанные сущности, затем удаляю старую сущность и пишу новую. Если у вас не так много отношений, это может быть легко, но если ваша модель данных сложна, вам лучше использовать описанное вами решение sql.
Вот источник, если вам интересно:
def convertToPacient = {
withPerson(params.id) {person ->
def pacient = new Pacient()
pacient.properties = person.properties
pacient.id = null
pacient.processNumber = params.processNumber
def ap = new Appointment(params)
pacient.addToAppointments(ap);
Person.withTransaction {tx ->
if (pacient.validate() && !pacient.hasErrors()) {
//to avoid the "Found two representations of same collection" error
//pacient.attachments = new HashSet(person.attachments);
//pacient.memberships = new HashSet(person.memberships);
def groups = person?.memberships?.collect {m -> Group.get(m.group.id)}
def attachs = []
person.attachments.each {a ->
def att = new Attachment()
att.properties = a.properties
attachs << att
}
//need an in in order to add the person to a group
person.delete(flush: true)
pacient.save(flush: true)
groups.each {g -> pacient.addToGroup(g)};
attachs.each {a -> pacient.addToAttachments(a)}
//pacient.attachments.each {att -> att.id = null; att.version = null; att.person = pacient};
if (!pacient.save()) {
tx.setRollbackOnly()
return
}
}
}
Когда вы говорите «преобразование», вы, вероятно, искажаете проблему. По моему мнению, вы действительно пытаетесь создать экземпляр одного класса на основе экземпляра другого класса, например:
public PayingUser(TrialUser theUser) {
...
}
Затем вы можете удалить старого пробного пользователя и сохранить нового платящего пользователя.
Проблема больше связана с Java чем что-либо еще. Вы не можете изменить тип времени выполнения экземпляра (при время выполнения), поэтому спящий режим не предусмотреть такие сценарии (в отличие от рельсов, например.)
Я столкнулся с этим сообщением, потому что у меня (или думал, что у меня есть) аналогичная проблема. Я пришел к выводу, что проблема заключается не в механике смены типов в технологии, а в том, что развитие типов в целом совсем не просто и непросто. Проблема в том, что типы довольно часто имеют разные атрибуты. Если вы не относитесь к разным типам только для различий в поведении, неясно, что делать с дополнительной информацией. По этой причине имеет смысл переносить типы на основе построения новых объектов.
Проблема не «связана с Java». это связано с теорией типов в целом и более конкретно здесь с объектно-реляционным отображением. Если ваш язык поддерживает набор текста, я Мне нравится слышать, как вы можете автоматически изменять тип объекта во время выполнения и волшебным образом делать что-то разумное с оставшейся информацией. Пока мы распространяем FUD: будьте осторожны, от кого вы берете совет: Rails - это фреймворк, Ruby - это язык.