Say I have two domain objects and a mapper interface.
class Person {
int id;
List<Problem> problems = new ArrayList<Problem>();
}
class Problem {
int id;
Person person;
}
interface PersonMapper {
public List<Person> selectAllPersons();
}
And two database tables.
create table person (
id integer not null generated always as identity constraint person_pk primary key,
)
create table problem (
id integer not null generated always as identity constraint problem_pk primary key,
person_id integer not null constraint problem_person_fk references person
)
I can create a mapping file that gets the data I want.
<resultMap id="personMap" type="Person">
<id column="person_id" property="id" />
<collection column="problem_person_id" property="problems"
javaType="ArrayList" ofType="Problem" resultMap="problemMap" />
</resultMap>
<resultMap id="problemMap" type="Problem">
<id column="problem_id" property="id" />
<!-- Adding an association here will cause a circular dependency -->
<!-- The circular dependency results in a StackOverflowException -->
</resultMap>
<select id="selectAllPersons" resultMap="personMap">
select
person.id as person_id,
problem.id as problem_id
from person left outer join problem on person.id = problem.person_id
</select>
However, since MyBatis doesn't do bi-directional mapping, none of the Problem objects in the returned collections will have their Person reference set correctly.
According to this issue, it sounds like I should be able to update my mapper interface and add a custom result handler that can be supplied by the calling class.
interface PersonMapper {
public List<Person> selectAllPersons(ResultHandler handler);
}
class PersonResultHandler implements ResultHandler {
#Override
public void handleResult(ResultContext context) {
System.out.println(context.getResultObject());
}
}
class PersonDAO {
// Get SqlSession sqlSession
sqlSession.getMapper(PersonMapper.class).selectAllPersons(new PersonResultHandler());
}
However, the handleResult method of my ResultHandler never gets called. I've seen this example, but the extra fluff class in there makes it very difficult to understand. Can anyone give me a simple example of using a custom ResultHandler with mapper interfaces? I'm using MyBatis 3.0.5+.
I've also read through the MyBatis mailing list and there are several suggestions of using caching and lazy loading to solve circular dependencies, but I can't find any examples of how to do it.
You should replace your method declaration to:
interface PersonMapper {
public void selectAllPersons(ResultHandler handler);
}
And populate List<Person> inside your PersonResultHandler
class PersonResultHandler implements ResultHandler {
List<Person> persons = new ArrayList<Person>();
#Override
public void handleResult(ResultContext context) {
Object result = context.getResultObject();
if (result instanceof Person) {
Person person = (Person) result;
for (Problem problem : person.getProblems()) {
problem.setPerson(person);
}
persons.add(person);
}
}
public List<Person> getPersons() {
return persons;
}
}
Related
I'm trying to save a list of entity objects. Here is my method which is defined in a Service class:
#Transactional
public void creteEmployee(List<Employee> employeeList) {
employeeRepository.saveAll(employeeList);
}
And here is my JPA repository:
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> {
Page<Employee> findByCompany(Pageable pageable, Company company);
}
I've a unique constraint defined in my entity. If any item from the employeeList violates the unique constraint, I expect no one from the employeeList will be saved. Unfortunately all items except the problematic one is saved and also an DataIntegrityViolationException is raised. What should be done to make it atomic?
I have a database table displayed through Datatable (Primefaces 3.4.2) and I want to show a comboFilter in the header populated with values from the database table itself.
1) Since these values are not a PK or FK, I built a named query to retrieve distinct values for the prefDep column:
#NamedQuery(name = "Upb.findPrefDeps", query = "SELECT DISTINCT u FROM Upb u WHERE u.prefDep = :prefDep")
2) In my AbstractController:
public List<T> getPrefDepsList() {
if (prefDeps == null) {
prefDeps = this.ejbFacade.findPrefDeps();
}
return prefDeps;
}
3) As I inject the facade EJB, how can I build a managed bean property to be used in the filterOption below?
The managedBean:
#ManagedBean(name = "upbController")
#ViewScoped
public class UpbController extends AbstractController<Upb> implements Serializable {
#EJB
private UpbFacade ejbFacade;
public UpbController() {
super(Upb.class);
}
#PostConstruct
public void init() {
super.setFacade(ejbFacade);
}
public SelectItem[] getPrefDepOptions() {
return prefDepOptions; //build/populate this
}
}
The jsf:
<p:column filterBy="prefdep" headerText="PrefDep"
filterOptions="#{upbController.prefDepOptions}"
filterMatchMode="exact">
<h:outputText value="#{item.prefDep}" />
</p:column>
Thanks in advance.
I do not know if I understand your question the right way. You want to execute the named query once and store the distinct values in a property in the managed bean? For that you can use a PreRenderView event which would be called before rendering the page. You can call a init-function to load such values with this event.
You can also access the getter with the named query, but this may be called not only once.
I'd like to create a generic C# class with a method that will add a row to a database using Entity Framework.
I have one table called Address. I've written the following code to add an address to the database:
public class AddressExchange
{
public int Insert(Address address)
{
using (var db = new DemoWebEntities())
{
//db.AddObject("Address", address);
db.Addresses.AddObject(address);
db.SaveChanges();
return address.Id;
}
}
}
I would like to write a generic class that will perform this operation for any entity in my EDMX. I think that it should look something like this:
public class EntityExchange<T, KeyType>
{
public KeyType Insert(T t)
{
using (var db = new DemoWebEntities())
{
// The entity set name might be wrong.
db.AddObject(typeof(T).Name, t);
// EF doesn't know what the primary key is.
return t.Id;
}
}
}
I think it may be possible to use the AddObject method to add the object to the database, but the entityset name is not necessarily the same as the type name, especially if it has been pluralized!
I also want to return the primary key to the caller, but I don't know how to tell which field contains the primary key.
I have a generic InsertOrUpdate method in a generic repository that also ensures proxies are created. (Proxies are required to support lazy loading and if you create an entity using "new", then proxies are not created). See the question here
public class RepositoryBase<T> : IRepository<T> where T : ModelBase
{
public virtual T InsertOrUpdate(T e)
{
DbSet<T> dbSet = context.Set<T>();
//Generate a proxy type to support lazy loading
T instance = dbSet.Create();
DbEntityEntry<T> entry;
if (e.GetType().Equals(instance.GetType()))
{
//The entity being added is already a proxy type that
//supports lazy loading just get the context entry
entry = context.Entry(e);
}
else
{
//The entity being added has been created using the "new" operator.
//Attach the proxy
//Need to set the ID before attaching or we get
//The property 'ID' is part of the object's key
//information and cannot be modified when we call SetValues
instance.ID = e.ID;
entry = context.Entry(instance);
dbSet.Attach(instance);
//and set it's values to those of the entity
entry.CurrentValues.SetValues(e);
e = instance;
}
entry.State = e.ID == default(int) ?
EntityState.Added :
EntityState.Modified;
return e;
}
}
public abstract class ModelBase
{
public int ID { get; set; }
}
Note that all the models inherit ModelBase so that handles the ID issue and I return the entity rather than just the ID. That is probably not strictly necessary since a reference to the entity is passed in and EF performs fixup on the ID anyway so you can always access it from the refernce passed in.
This might be reliant on a particular version on Entity framework however this is how I do it
public void Create(T entity)
{
using (var db = new DemoWebEntities())
{
db.Set<T>().Add(entity);
}
}
For the primary key issue, can you use partial classes to make your entities implement an interface, something like this:
public interface IEntity
{
Guid PrimaryKey { get; }
}
Your entity classes would then return the appropriate value:
public partial class EntityType : IEntity
{
public Guid PrimaryKey
{
get
{
return this.WhateverId; // Return the primary key
}
}
}
Then, constrain your method to only accept IEntity:
public class EntityExchange<T, KeyType> where T : IEntity
And finally return the primary key after the insert:
return t.PrimaryKey;
May be it can help you.
public T Add(T model)
{
using (BigConceptEntities entity = new BigConceptEntities())
{
entity.Set<T>().Add(model);
entity.SaveChanges();
return model;
}
}
Good evening everybody, this is my first post on Stack Overflow.
I have been quite recently introduced to Java 6 EE and, in particular, to JPA as part of the JSF 2.1 framework and I am now facing a strange behavior that I would like you to help me understand.
In our project (developed using NetBeans 7.2) we have several one-to-many relationship and we would like to navigate them the same way we navigate many-to-one ones. The fact is that, instead, we are able to make them work as we want only after having restarted the application server (Glassfish 3.1.2) and, in addition, this behavior lasts only till the next deployment; which means we need to restart Glassfish every time we apply a modification...
Here are some code excerpts to help you understand our situation.
This represents our main entity (Person) that has, among the others, a one-to-many relationship with Email as well as with Phone and a many-to-one relationship with AccountType
#Entity
public class Person implements Serializable {
//
//private non-collection fields including id
//
#OneToMany(mappedBy="person", fetch=FetchType.EAGER)
private Collection<Email> personEmails;
#OneToMany(mappedBy="person")
private Collection<Phone> personPhones;
#ManyToOne
private AccountType accountType;
//
// getter and setter, hashCode, isEqual and toString
//
}
And these are Email...
#Entity
public class Email implements Serializable {
//
//private non-collection fields including id
//
private String address;
#ManyToOne
private Person person;
//
// getter and setter, hashCode, isEqual and toString
//
}
... Phone ...
#Entity
public class Phone implements Serializable {
//
//private non-collection fields including id
//
private String number;
#ManyToOne
private Person person;
//
// getter and setter, hashCode, isEqual and toString
//
}
... and AccountType
#Entity
public class AccounType implements Serializable {
//
//private non-collection fields including id
//
private String name;
#OneToMany(mappedBy="accountType")
private Collection<Person> persons;
//
// getter and setter, hashCode, isEqual and toString
//
}
We have then set up a sample page to test how that three fields in Person are actually fetched.
This represents the xhtml page...
<h:form id="form">
<h:panelGrid columns="2">
<h:outputLabel value="forename" />
<h:outputLabel value="#{meBean.currentUser.forename}" />
<h:outputLabel value="emails" />
<h:outputLabel value="#{meBean.currentUser.personEmails.size()}" />
<h:outputLabel value="phones" />
<h:outputLabel value="#{meBean.currentUser.personPhones}" />
<h:outputLabel value="accountType" />
<h:outputLabel value="#{meBean.currentUser.accountType.name}" />
</h:panelGrid>
</h:form>
... and this the controller
#ManagedBean
#RequestScoped
public class MeBean {
#EJB
private PersonFacade personFacade;
private Person currentUser;
public MeBean() {
init();
}
#PostConstruct
private void init() {
// Hard-coding user details
try {
this.currentUser = this.personFacade.getFromUsername("user1");
this.currentUser.getPersonPhones().isEmpty();
} catch (Exception e) {
}
}
public Person getCurrentUser() {
return currentUser;
}
public void setCurrentUser(Person currentUser) {
this.currentUser = currentUser;
}
}
Now, the result we get is the one we expect only if we access the page right after having restarted the application server.
forename Alice
emails 2
phones {[sums.groupa.entities.Phone[id=38]]}
accountType Student
If we modify anything (except for the view) and save, after the inevitable deploy, the result is different.
forename Alice
emails 0
phones {[]}
accountType Student
Why is that happening and how can we avoid it?
Thanks in advance.
AG
A couple of contributors (that I want to thank for their quick replies) asked for the PersonFacade implementation.
public Person getFromUsername(String username)
{
try
{
Query q = em.createQuery("SELECT p FROM Person p LEFT JOIN FETCH p.personEmails WHERE UPPER(p.username) = :username");
q.setParameter("username", username.toUpperCase());
return (Person) q.getSingleResult();
}
catch (Exception ex)
{
Logger.getLogger(PersonFacade.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
}
As you can see I tried to use FETCH JOIN as suggested but the query is getting out too many results: it should fetch only one instance of Person representing Alice and containing two instances of Email in the personEmails field but I suspect it is getting two different instances of Person, each having a different instance of Email attached.
The original query was as follows:
SELECT p FROM Person p WHERE UPPER(p.username) = :username
Thanks again.
AG
I don't know how you wrote your personFacade and got the Person entity.
But I guess you used query.
If you are using JPQL, try fetch join.
I have this simple Delete Get and Post methods in a asp.net mvc application
public ActionResult Delete(int ehrId, int id)
{
EHR ehr = ehrRepository.FindById(ehrId);
PhysicalTest test = ehr.PhysicalTests.Where(t => t.ID == id).Single();
return View(test);
}
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int ehrId, int id)
{
EHR ehr = ehrRepository.FindById(ehrId);
PhysicalTest test = ehr.PhysicalTests.Where(t => t.ID == id).Single();
ehr.PhysicalTests.Remove(test);
unitOfWork.Commit();
TempData["Success"] = "You have deleted the Physical Test Succesfully";
return RedirectToAction("Index");
}
the problem is that when I try to delete a child object this way and EF will complain
The operation failed: The relationship could not be changed because
one or more of the foreign-key properties is non-nullable. When a
change is made to a relationship, the related foreign-key property is
set to a null value. If the foreign-key does not support null values,
a new relationship must be defined, the foreign-key property must be
assigned another non-null value, or the unrelated object must be
deleted.
One answer is to use a PhysicalTest(child element) repository instead of a EHRRepository.. but that doesnt seem like a good solution cause I want to enforce the security of always querying through the parent object to avoid a user from editing/deleting a physicalTest that doesnt belong to him.
I would love to just limit my repositories to just aggregate roots.
Heres my current SqlRepository generic implementation.... Im open to suggestions.
public class SqlRepository<T> : IRepository<T>
where T : class, IEntity {
internal SummumnetDB context;
internal DbSet<T> _objectSet;
public SqlRepository(SummumnetDB context)
{
this.context = context;
this._objectSet = context.Set<T>();
}
public IQueryable<T> Find(Expression<Func<T, bool>> predicate) {
return _objectSet.Where(predicate);
}
public void Add(T newEntity) {
_objectSet.Add(newEntity);
}
public void Remove(T entity) {
_objectSet.Remove(entity);
}
public IQueryable<T> FindAll()
{
return _objectSet;
}
public T FindById(int id)
{
return _objectSet.Single(o => o.ID == id);
}
}
Your EHR and PhysicalTest forms aggregate where EHR is aggregate root for PhysicalTest because PhyscialTest cannot exist without EHR (your exception says that FK in PhysicalTest cannot be null). Repository should exist per aggregate root and it should offer specific method to deal with relations.
Yes it will not be generic because generic approach for entities with different configurations and requirements doesn't work.
What is the problem with your code? Calling ehr.PhysicalTests.Remove(test) will not delete test. It will only sets its FK to null. To delete test as well you must really call context.DeleteObject(test). To allow direct deleting you must use identifying relation.